如何比较两个XML文档?

70
作为某些广泛单元测试的基类的一部分,我正在使用C# (.NET)编写一个帮助函数,该函数递归比较一个XmlDocument对象的节点与另一个对象。具体要求如下:
  • 第一个文档是源文件,例如,我想让XML文档看起来像什么。因此第二个文档是我想要查找差异的文档,并且它不应包含第一个文档中没有的额外节点。
  • 在发现太多重大差异时必须抛出异常,并且应该很容易被人类查看描述所理解。
  • 子元素顺序很重要,属性可以按任意顺序排列。
  • 某些属性是可忽略的;特别是xsi:schemaLocation和xmlns:xsi,尽管我希望能够传入哪些属性是可忽略的。
  • 命名空间的前缀必须匹配属性和元素。
  • 元素之间的空格无关紧要。
  • 元素将只有子元素或InnerText,但不会同时存在。

在我刚开始写这个东西时:是否有人编写过这样的代码,是否可能在这里共享它?

顺便问一下,你会如何称呼第一个和第二个文档?我一直称它们为“源”和“目标”,但感觉不太对,因为源是我想要目标看起来像什么,否则就会抛出异常。


节点可以相同但声明的顺序不同吗? - alexmac
不,节点必须按相同顺序排列。除了文档本身的要求外,这样做还可以使差异比较变得更简单(只需枚举子项并逐一检查)。 - Neil C. Obremski
好事,因为属性根据定义是无序的。 - Robert Rossney
可能是重复的问题:什么是比较XML文件相等的最佳方法? - Andrej Adamenko
1
称它们为“实际”和“预期”(是的,我知道我晚了13年)。 - Dawood ibn Kareem
显示剩余2条评论
13个回答

1

以上所有答案都很有帮助,但我尝试了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


0
基于@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的方法。另外,为什么不对两个文档进行排序,而不是在循环中使用排序呢? - Pankaj Jaju
@PankajJaju 我知道 XSLT 更快,但我没有 XSLT 编程的知识。同时,我正在对两个文档进行排序,并将两个文档的根元素作为 compareXML 方法的第一个节点进行调用:compareXML(document1.rootnode, document2.rootnode); 并对两个文档的每个节点进行排序。 - Chetan Mehra
请查看我的答案,其中包含了一个针对此问题的 XSLT 1.0 解决方案。 - Stephen Flynn

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接