【问题标题】:How would you compare two XML Documents?您将如何比较两个 XML 文档?
【发布时间】:2010-09-15 03:22:12
【问题描述】:

作为一些扩展单元测试的基类的一部分,我正在编写一个辅助函数,它递归地将一个 XmlDocument 对象的节点与 C# (.NET) 中的另一个对象进行比较。对此的一些要求:

  • 第一个文档是来源,例如我希望 XML 文档看起来像什么。因此,第二个是我想要找到差异的那个,它不能包含不在第一个文档中的 extra 节点。
  • 当发现太多显着差异时必须抛出异常,并且应该很容易通过人类的描述来理解。
  • 子元素顺序很重要,属性可以是任意顺序。
  • 有些属性是可以忽略的;特别是xsi:schemaLocationxmlns:xsi,虽然我希望能够传递哪些是。
  • 命名空间的前缀必须在属性和元素中匹配。
  • 元素之间的空格无关紧要。
  • 元素将要么有子元素InnerText,但不能同时拥有。

当我一起废弃一些东西时:有人写过这样的代码吗?可以在这里分享吗?

顺便说一句,你会怎么称呼第一个和第二个文件?我一直将它们称为“源”和“目标”,但感觉不对,因为 是我希望 目标 看起来的样子,否则我会抛出一个例外。

【问题讨论】:

  • 节点可以相同但声明顺序不同吗?
  • 不,节点的顺序必须相同。除了作为文档本身的要求之外,它还使区分变得更简单(只需枚举孩子并一对一检查)。
  • > 属性可以按任何顺序排列 好东西,因为根据定义,属性是无序的。
  • 我将文档称为“基线”和“测试”。
  • 称它们为“实际”和“预期”(是的,我知道我已经晚了 13 年)。

标签: c# .net xml compare xmldocument


【解决方案1】:

Microsoft 有一个您可以使用的XML diff API

非官方 NuGet:https://www.nuget.org/packages/XMLDiffPatch.

【讨论】:

  • 这很酷!不幸的是,它没有做的一件事是允许我忽略某些属性。
  • 我忘了在我的帖子中提到,我在 XSLT 中做的另一件事是过滤掉某些属性。
  • 该工具的另一个链接在这里msdn.microsoft.com/en-gb/library/aa302294.aspx
  • 对我来说效果很好,但我确实遇到了与 cmets 相关的错误(即使未指定 XmlDiffOptions.IgnoreComments,它也会忽略 cmets)
  • 请注意,XML Notepad (github.com/microsoft/XmlNotepad) 内置了XMLDiff,它直观地显示了编码在 XMLDiff diffgram 中的差异。只需打开一个 XML,然后转到 查看 -> 比较 XML 文件。您甚至可以在选项中控制XmlDiffOptions
【解决方案2】:
【解决方案3】:

此代码不能满足您的所有要求,但它很简单,我正在用于我的单元测试。属性顺序无关紧要,但元素顺序很重要。不比较元素内部文本。在比较属性时我也忽略了大小写,但您可以轻松删除它。

public bool XMLCompare(XElement primary, XElement secondary)
{
    if (primary.HasAttributes) {
        if (primary.Attributes().Count() != secondary.Attributes().Count())
            return false;
        foreach (XAttribute attr in primary.Attributes()) {
            if (secondary.Attribute(attr.Name.LocalName) == null)
                return false;
            if (attr.Value.ToLower() != secondary.Attribute(attr.Name.LocalName).Value.ToLower())
                return false;
        }
    }
    if (primary.HasElements) {
        if (primary.Elements().Count() != secondary.Elements().Count())
            return false;
        for (var i = 0; i <= primary.Elements().Count() - 1; i++) {
            if (XMLCompare(primary.Elements().Skip(i).Take(1).Single(), secondary.Elements().Skip(i).Take(1).Single()) == false)
                return false;
        }
    }
    return true;
}

【讨论】:

  • 我无法将属性添加到 Count()
  • @Prof.Falkencontractbreached 您是否使用了正确的 XElement?您应该使用 System.Xml.Linq 中的那些。
  • @GilesRoberts 本来可以的。有一段时间了,但我记得被同名的类型弄糊涂了。
  • 出于我的目的,我还对 XML 元素本身的值感兴趣。 if (primary.Value != secondary.Value) { return false; }
【解决方案4】:

试试XMLUnit。该库适用于 Java 和 .Net

【讨论】:

    【解决方案5】:

    比较 XML 文档很复杂。 Google for xmldiff(甚至还有 Microsoft 解决方案)用于某些工具。我已经解决了这几种方法。我使用 XSLT 对元素和属性进行排序(因为有时它们会以不同的顺序出现,我并不关心),并过滤掉我不想比较的属性,然后使用 XML::DiffXML::SemanticDiff perl 模块,或者漂亮地打印每个文档,每个元素和属性在单独的行上,并使用 Unix 命令行 diff 对结果。

    【讨论】:

      【解决方案6】:

      为了在自动化测试中比较两个 XML 输出,我找到了XNode.DeepEquals

      比较两个节点的值,包括所有后代节点的值。

      用法:

      var xDoc1 = XDocument.Parse(xmlString1);
      var xDoc2 = XDocument.Parse(xmlString2);
      
      bool isSame = XNode.DeepEquals(xDoc1.Document, xDoc2.Document);
      //Assert.IsTrue(isSame);
      

      参考:https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xnode.deepequals?view=netcore-2.2

      【讨论】:

      【解决方案7】:

      https://github.com/CameronWills/FatAntelope Microsoft XML Diff API 的另一个替代库。它有一个 XML diffing 算法,可以对两个 XML 文档进行无序比较并产生最佳匹配。

      它是此处描述的 X-Diff 算法的 C# 端口: http://pages.cs.wisc.edu/~yuanwang/xdiff.html

      免责声明:我写的:)

      【讨论】:

        【解决方案8】:

        另一种方法是 -

        1. 将两个文件的内容放入两个不同的字符串中。
        2. 使用 XSLT 转换字符串(它只会将所有内容复制到两个新字符串中)。这将确保元素外的所有空格都被删除。这将产生两个新字符串。
        3. 现在,只需将两个字符串相互比较即可。

        这不会为您提供差异的确切位置,但如果您只想知道是否存在差异,无需任何第三方库即可轻松完成。

        【讨论】:

        • 这并没有回答特定的问题,但这个概念与问题中提出的问题相关。我的 +1。
        【解决方案9】:

        我正在使用ExamXML 来比较 XML 文件。你可以试试看。 作者 A7Soft 还提供了用于比较 XML 文件的 API

        【讨论】:

          【解决方案10】:

          与 OP 无关,因为它目前忽略子订单,但如果您想要一个纯代码解决方案,您可以尝试我 somewhat misguidedly 开发的 XmlSpecificationCompare

          【讨论】:

            【解决方案11】:

            以上所有答案都有帮助,但我尝试了XMLUnit,它看起来很容易使用 Nuget 包来检查两个 XML 文件之间的差异,这里是 C# 示例代码

            public static bool CheckXMLDifference(string xmlInput, string xmlOutput)
                {
                    Diff myDiff = DiffBuilder.Compare(Input.FromString(xmlInput))
                        .WithTest(Input.FromString(xmlOutput))
                        .CheckForSimilar().CheckForIdentical()
                        .IgnoreComments()
                        .IgnoreWhitespace().NormalizeWhitespace().Build();
            
                    if(myDiff.Differences.Count() == 0)
                    {
                        // when there is no difference 
                        // files are identical, return true;
                        return true;
                    }
                    else
                    {
                        //return false when there is 1 or more difference in file
                        return false;
                    }
            
                }
            

            如果有人想测试它,我也用它创建了在线工具,你可以看看这里

            https://www.minify-beautify.com/online-xml-difference

            【讨论】:

              【解决方案12】:

              基于@Two Cents 的回答并使用此链接XMLSorting 我创建了自己的 XmlComparer

              比较 XML 程序

              private static bool compareXML(XmlNode node, XmlNode comparenode)
                  {
              
                      if (node.Value != comparenode.Value)
                          return false;
              
                          if (node.Attributes.Count>0)
                          {
                              foreach (XmlAttribute parentnodeattribute in node.Attributes)
                              {
                                  string parentattributename = parentnodeattribute.Name;
                                  string parentattributevalue = parentnodeattribute.Value;
                                  if (parentattributevalue != comparenode.Attributes[parentattributename].Value)
                                  {
                                      return false;
                                  }
              
                              }
              
                          }
              
                        if(node.HasChildNodes)
                          {
                          sortXML(comparenode);
                          if (node.ChildNodes.Count != comparenode.ChildNodes.Count)
                              return false;
                          for(int i=0; i<node.ChildNodes.Count;i++)
                              {
              
                              string name = node.ChildNodes[i].LocalName;
                              if (compareXML(node.ChildNodes[i], comparenode.ChildNodes[i]) == false)
                                  return false;
                              }
              
                          }
              
              
              
                      return true;
                  }
              

              对 XML 程序进行排序

               private static void sortXML(XmlNode documentElement)
                  {
                      int i = 1;
                      SortAttributes(documentElement.Attributes);
                      SortElements(documentElement);
                      foreach (XmlNode childNode in documentElement.ChildNodes)
                      {
                          sortXML(childNode);
              
                      }
                  }
              
              
              
                private static void SortElements(XmlNode rootNode)
                  {
              
              
              
                          for(int j = 0; j < rootNode.ChildNodes.Count; j++) {
                              for (int i = 1; i < rootNode.ChildNodes.Count; i++)
                              {
                                  if (String.Compare(rootNode.ChildNodes[i].Name, rootNode.ChildNodes[1 - 1].Name) < 0)
                                  {
                                      rootNode.InsertBefore(rootNode.ChildNodes[i], rootNode.ChildNodes[i - 1]);
              
                                  }
              
              
                              }
                          }
                         // Console.WriteLine(j++);
              
              
                  }
               private static void SortAttributes(XmlAttributeCollection attribCol)
                  {
                      if (attribCol == null)
                          return;
                      bool changed = true;
                      while (changed)
                      {
                          changed = false;
                          for (int i = 1; i < attribCol.Count; i++)
                      {
                              if (String.Compare(attribCol[i].Name, attribCol[i - 1].Name) < 0)
                              {
                                  //Replace
                                  attribCol.InsertBefore(attribCol[i], attribCol[i - 1]);
                                  changed = true;
              
                              }
                          }
                      }
                  }
              

              【讨论】:

              • XSLT 是一种更快的 xml 排序方式。另外,为什么不对两个文档进行排序,而不是在循环中使用排序?
              • @PankajJaju 我知道 xslt 更快,但我没有任何 xslt 编程知识,我也在对两个文档进行排序,我将两个文档的根元素称为 compareXML 的第一个节点方法` compareXML(document1.rootnode, document2.rootnode);` 并对两个文档的每个节点进行排序
              • 查看我的答案以获取针对此问题的 xslt 1.0 解决方案。
              【解决方案13】:

              我使用 XSLT 1.0 解决了这个 xml 比较问题,它可用于使用无序树比较算法比较大型 xml 文件。 https://github.com/sflynn1812/xslt-diff-turbo

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-10-29
                • 1970-01-01
                • 1970-01-01
                • 2010-09-10
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多