【问题标题】:use LINQ to XML to Programmatically get all differences between 2 xml files使用 LINQ to XML 以编程方式获取 2 个 xml 文件之间的所有差异
【发布时间】:2015-08-27 10:38:39
【问题描述】:

我正在使用 c sharp 5.0 并使用 LINQ to XML 我尝试以编程方式获取 2 个 xml 文件之间的差异 我的第一个文件很简单:

<?xml version="1.0" encoding="UTF-8" ?> 
<table name="MYTABLE">
<columns>
<column name="MY_ID" type="NUMBER"/> 
<column name="M_NAME" type="VARCHAR2" /> 
<column name="MY_PARENT_ID" type="VARCHAR2" /> 
<column name="MY_CAPTION" type="VARCHAR2" />
</columns>
</table>

我的第二个 xml 文件很简单:

<?xml version="1.0" encoding="UTF-8" ?> 
<table name=" MYTABLE ">
<columns>
<column name="MY_ID" type="NUMBER"/> 
<column name="M_NAME" type="VARCHAR2" /> 
<column name="MY_PARENT_ID" type="NUMBER" />
</columns>
</table>

我的代码很简单:

XDocument doc1 = XDocument.Load(@"Path\table1.xml");
XDocument doc2 = XDocument.Load(@"Path\table2.xml");
var cols1 = doc1.Descendants("column") ;
var cols2 = doc2.Descendants("column") ;
var itemsOnFirstFileOnly = cols1.Except(cols2);
var itemsOnSecondFileOnly = cols2.Except(cols1);

但令我惊讶的是 itemsOnFirstFileOnly 返回了 table1.xml 上存在的所有列(不仅是 MY_PARENT_ID 和 MY_CAPTION ),并且 itemsOnSecondFileOnly 返回了 table2.xml 中存在的所有列(不仅是 MY_PARENT_ID),所以为什么不除外呢?没有按预期工作??

【问题讨论】:

标签: c# xml linq except


【解决方案1】:

您必须在您的 Column 类上实现 Equals,以便 Except 知道两个 Columns 何时相等:

class Column 
{
    bool Equals(object other) 
    {
        // ...
        // skip further checks for simplicity
        // ...

        Column o = (Column) other;
        return o.Name == this.Name && o.Type == this.Type  ;
    }
}

如果您不实施此方法,Except 将只检查引用相等性,这永远不会是真的,因为您的列来自不同的上下文(在您的情况下是文件)。

编辑:当您比较XNodes 时,事情要容易得多,因为您可以使用XNodeEqualityComparer-class,它有自己的Equals-实现,完全符合您的需求,如here 所示。因此,以下应该有效:

var itemsOnFirstFileOnly = cols1.Except(cols2, new XNodeEqualityComparer());

【讨论】:

  • 看起来 OP 正在比较 XNode。无法在此处轻松提供 Equals 的实现。
  • @Corak 是的,你是对的,希望现在更新是正确的。
  • 是的,在我将“new XNodeEqualityComparer”参数添加到Except方法后它就可以正常工作,而无需实现Equals,非常感谢!
【解决方案2】:

您可以使用 Linq 做到这一点:

var itemsOnFirstFileOnly = cols1.Where(x => !cols2.Select(y => y.Attribute("name").Value).Contains(x.Attribute("name").Value));
var itemsOnSecondFileOnly = cols2.Where(x => !cols1.Select(y => y.Attribute("name").Value).Contains(x.Attribute("name").Value));

基本上在上面的代码中:

  • 选择name 属性的值
  • 比较这些名称
  • 返回其他集合中缺少名称的 XElement 实例。

【讨论】:

    【解决方案3】:

    您还可以将XElements 转换为字符串,这会将属性考虑在内:

    XDocument doc = XDocument.Parse(xml);
    XDocument doc2 = XDocument.Parse(xml2);
    
    var diffs = doc.Descendants().Select(e => e.ToString())
                .Except(doc2.Descendants().Select(e => e.ToString()));
    
    foreach (var e in diffs)
    {
        Console.WriteLine(e);
        Console.WriteLine();
    }
    

    考虑列,这只返回

    <column name="MY_PARENT_ID" type="VARCHAR2" />
    
    <column name="MY_CAPTION" type="VARCHAR2" />
    

    【讨论】:

    • 我强烈建议不要在此处(以及大多数其他地方)使用string 表示进行相等检查,因为它的性能很差。在这种情况下,您的 docA.Descendants().Select(e =&gt; e.ToString()).Except(docB.Descendants().Select(e =&gt; e.ToString()))docA.Descendants().Except(docB.Descendants(), new XNodeEqualityComparer()) 慢大约 16 倍。
    猜你喜欢
    • 2013-03-25
    • 2023-04-06
    • 1970-01-01
    • 2013-06-05
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    相关资源
    最近更新 更多