67

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

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

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

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

4

13 回答 13

59

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

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

于 2008-10-03T17:22:46.397 回答
6

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

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;
}
于 2015-05-05T14:57:29.653 回答
5

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

于 2008-10-03T17:28:01.940 回答
5

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

于 2011-11-23T13:45:37.867 回答
5

为了比较自动化测试中的两个 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

于 2018-12-20T14:15:04.320 回答
4

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

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

免责声明:我写的:)

于 2015-11-07T07:08:04.683 回答
3

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

于 2009-11-16T14:20:41.140 回答
3

另一种方法是 -

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

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

于 2010-08-30T19:20:29.150 回答
2

与 OP 无关,因为它目前忽略子订单,但如果您想要一个仅代码的解决方案,您可以尝试XmlSpecificationCompare,我有点误导地开发了它。

于 2013-09-01T12:40:12.093 回答
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

于 2021-03-09T15:08:43.810 回答
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;

                }
            }
        }
    }
于 2017-09-08T12:55:18.683 回答
0

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

于 2019-04-23T11:59:36.937 回答