Linq:比较2个XML文件并将差异输出到xml [英] Linq: Comparing 2 XML files and outputting to xml the differences

查看:62
本文介绍了Linq:比较2个XML文件并将差异输出到xml的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有2个xml文件,当前版本(B)和先前版本(A)

I have an 2 xml files, the Current Version (B) and the previous version (A)

我想检查是否已根据Id属性更改了任何属性 如果是这样,我想获取该元素以及已添加到当前文件(B)中的所有新元素

I want to check if any of the Attributes have changed based on the Id Attribute If so I want to grab that Element and also any new Elements that have been added to the current file (B)

因此,我将得到一个包含任何更改和任何新元素的元素的xml文件

So I would have a resulting xml file of any element with any changes and any new element

---- Version A
<Books>
    <book id='1' image='C01' name='C# in Depth'/>
    <book id='2' image='C02' name='ASP.NET'/>
    <book id='3' image='C03' name='LINQ in Action '/>
    <book id='4' image='C04' name='Architecting Applications'/>
</Books>
---- Version B
<Books>    
 <book id='1' image='C011' name='C# in Depth'/>
 <book id='2' image='C02' name='ASP.NET 2.0'/>
 <book id='3' image='XXXC03' name='XXXLINQ in Action '/>
 <book id='4' image='C04' name='Architecting Applications'/>
<book id='5' image='C05' name='PowerShell in Action'/>
</Books>

我想返回以下内容

 ---- Results
 <Books>
  <book id='1' image='C011' name='C# in Depth'/>
  <book id='2' image='C02' name='ASP.NET 2.0'/>
  <book id='3' image='XXXC03' name='XXXLINQ in Action '/>
  <book id='5' image='C05' name='PowerShell in Action'/>
</Books>

到目前为止,这是我的代码.我可以基于id来获取更改,但不能基于任何新ID进行更改,而且我确信有人可以在一个语句和属性中获取全部内容,而无需再次解析. 谢谢

Here is my code so far. I can get the changes based on the ids but not any new ones and also I'm sure someone can get the whole lot out in one statment and also the attributes without having to parse again. Thanks

 private void LinqCompareXMLFiles() 
        {
            string oldXML = @"<Books>
     <book id='1' image='C01' name='C# in Depth'/>
     <book id='2' image='C02' name='ASP.NET'/>
     <book id='3' image='C03' name='LINQ in Action '/>
     <book id='4' image='C04' name='Architecting Applications'/>

    </Books>";

            string newXML = @"<Books>
     <book id='1' image='C011' name='C# in Depth'/>
     <book id='2' image='C02' name='ASP.NET 2.0'/>
     <book id='3' image='XXXC03' name='XXXLINQ in Action '/>
     <book id='4' image='C04' name='Architecting Applications'/>
    <book id='5' image='C05' name='PowerShell in Action'/>

    </Books>";

            XDocument xmlOld = XDocument.Parse(oldXML);
            XDocument xmlNew = XDocument.Parse(newXML);

            var res = (from b1 in xmlOld.Descendants("book")
                       from b2 in xmlNew.Descendants("book")
                      let issues = from a1 in b1.Attributes()
                                   join a2 in b2.Attributes()
                                     on a1.Name equals a2.Name
                                   select new
                                   {
                                       Id = a1.Parent.FirstAttribute.Value,
                                       Name = a1.Name,
                                       Value1 = a1.Value,
                                       Value2 = a2.Value
                                   }
                      where issues.Any(i => i.Value1 == i.Value2)
                      from issue in issues
                      where issue.Value1 != issue.Value2
                      select issue);
            var reportXmlItems = (from rx in res select new XElement("book", new XAttribute("id", rx.Id))).Distinct(new MyComparer());

            // This isn't excluding the ids that exist in theold book set because they are different elements I guess and I need to exclude based on the element Id
            var res2 = (from b2 in xmlNew.Descendants("book") select new XElement("book", new XAttribute("id",b2.Attribute("id").Value))).Except(xmlOld.Descendants("book"));

            var res3 = reportXmlItems.Union(res2);

            var reportXml = new XElement("books", res3);
            reportXml.Save(@"c:\test\result.xml");
        }

    public class MyComparer : IEqualityComparer<XElement>
    {
        public bool Equals(XElement x, XElement y)
        {
            return x.Attribute("id").Value == y.Attribute("id").Value;
        }

        public int GetHashCode(XElement obj)
        {
            return obj.Attribute("id").Value.GetHashCode();
        }
    }

推荐答案

我看不出比较具有相同ID的节点的意义-可以直接更改它们.但是话虽如此,您可以使用LINQ to XML比较并合并XML文档,如下所示:

I don't see the point to comparing nodes with same id - they can be changed directly. But having said that, you can compare and merge your XML documents using LINQ to XML as follows:

// XMLs
string oldXML = @"<Books>
<book id='1' image='C01' name='C# in Depth'/>
<book id='2' image='C02' name='ASP.NET'/>
<book id='3' image='C03' name='LINQ in Action '/>
<book id='4' image='C04' name='Architecting Applications'/>
</Books>";
string newXML = @"<Books>
<book id='1' image='C011' name='C# in Depth'/>
<book id='2' image='C02' name='ASP.NET 2.0'/>
<book id='3' image='XXXC03' name='XXXLINQ in Action '/>
<book id='4' image='C04' name='Architecting Applications'/>
<book id='5' image='C05' name='PowerShell in Action'/>
</Books>";

代码:

// xml documents
var xmlOld = XDocument.Parse(oldXML);
var xmlNew = XDocument.Parse(newXML);
// helper function to get the attribute value of the given element by attribute name
Func<XElement, string, string> getAttributeValue = (xElement, name) => xElement.Attribute(name).Value;
// nodes for which we are looking for
var nodeName = "book";
var sameNodes = new List<string>();
// iterate over all old nodes (this will replace all existing but changed nodes)
xmlOld.Descendants(nodeName).ToList().ForEach(item =>
{
    var currentElementId = getAttributeValue(item, "id");
    // find node with the same id in the new nodes collection
    var toReplace = xmlNew.Descendants(nodeName).ToList().FirstOrDefault(n => getAttributeValue(n, "id") == currentElementId);
    if (toReplace != null)
    {
        var aImageOldValue = getAttributeValue(item, "image");
        var aImageNewValue = getAttributeValue(toReplace, "image");
        var aNameOldValue = getAttributeValue(item, "name");
        var aNameNewValue = getAttributeValue(toReplace, "name");
        if ((aImageNewValue != aImageOldValue) || (aNameOldValue != aNameNewValue))
        {
            // replace attribute values
            item.Attribute("image").Value = getAttributeValue(toReplace, "image");
            item.Attribute("name").Value = getAttributeValue(toReplace, "name");
        }
        else if ((aImageNewValue == aImageOldValue) && (aNameOldValue == aNameNewValue))
        {
            // remove same nodes! can't remove the node yet, because it will be seen as new
            sameNodes.Add(getAttributeValue(item, "id"));
        }
    }
});
// add new nodes
// id's of all old nodes
var oldNodes = xmlOld.Descendants(nodeName).Select (node => getAttributeValue(node, "id")).ToList();
// id's of all new nodes
var newNodes = xmlNew.Descendants(nodeName).Select (node => getAttributeValue(node, "id")).ToList();
// find new nodes that are not present in the old collection
var nodeIdsToAdd = newNodes.Except(oldNodes);
// add all new nodes to the already modified xml document
foreach (var newNodeId in nodeIdsToAdd)
{
    var newNode = xmlNew.Descendants(nodeName).FirstOrDefault(node => getAttributeValue(node, "id") == newNodeId);
    if (newNode != null)
    {
        xmlOld.Root.Add(newNode);
    }
}
// remove unchanged nodes
foreach (var oldNodeId in sameNodes)
{
    xmlOld.Descendants(nodeName).FirstOrDefault (node => getAttributeValue(node, "id") == oldNodeId).Remove();
}
xmlOld.Save(@"d:\temp\merged.xml");

生成的XML看起来像这样:

The resulting XML looks like this:

<Books>
  <book id="1" image="C011" name="C# in Depth" />
  <book id="2" image="C02" name="ASP.NET 2.0" />
  <book id="3" image="XXXC03" name="XXXLINQ in Action " />
  <book id="5" image="C05" name="PowerShell in Action" />
</Books>

这篇关于Linq:比较2个XML文件并将差异输出到xml的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆