比较XmlDocument的相等性(内容方面)

21

如果我想比较一个XML文档的内容,是不是就像这样做?

XmlDocument doc1 = GetDoc1();
XmlDocument doc2 = GetDoc2();

if(doc1 == doc2)
{

}

我不是在检查它们是否是同一个对象引用,而是检查xml的内容是否相同。

6个回答

42

非常好。我不知道这个存在。它看起来处理了我描述的许多情况。 - LBushkin
太棒了——这意味着我不需要导入第三方库来完成这个任务! - Sebastian Patten

13

不,XmlDocument没有覆盖Equals()方法的行为,因此实际上只执行引用相等性-这在您的示例中将失败,除非文档实际上是相同的对象实例。

如果你想比较一个文档的内容(属性、元素、注释、PI等),你必须自己实现逻辑。请注意:这并不容易。

根据您的具体情况,您可以从文档中删除所有非必要的空格(这本身可能很棘手),然后比较生成的xml文本。这并不完美-对于在语义上相同但在使用和声明命名空间的方式、某些值是否转义或者元素顺序等方面略有不同的文档会失败。正如我之前所说,XML比较并不简单。

你还需要清楚地定义两个XML文档“相同”的含义。元素或属性排序重要吗?大小写(在文本节点中)有关系吗?应该忽略多余的CDATA部分吗?处理指令算吗?全限定与部分限定的命名空间呢?

在任何通用的实现中,您可能需要将两个文档转换为一些规范形式(无论是XML还是其他表示形式),然后比较规范化的内容。

已经存在执行XML差异的工具,如Microsoft XML Diff/Patch,您可以利用它来识别两个文档之间的差异。据我所知,该工具未以源代码形式分发...因此,在嵌入式应用程序中使用它,您需要编写脚本过程(如果您计划使用它,请首先验证许可条款是否允许使用和再发布)。

编辑:如果你正在使用.NET 3.5 SP1,查看@Max Toro的回答,因为显然在XLinq中有一个选项可能会有所帮助。很高兴知道它的存在。


11

一个简单的方法是比较 OuterXml

var a = new XmlDocument();
var b = new XmlDocument();

a.LoadXml("<root  foo='bar'  />");
b.LoadXml("<root foo='bar'/>");

Debug.Assert(a.OuterXml == b.OuterXml);

0

通常您希望比较以不同顺序排列的XML字符串。使用此代码可以轻松完成。

class Testing
{
    [Test]
    public void Test()
    {
        Assert.AreEqual(
            "<root><a></a><b></b></root>".SortXml()
            , "<root><b></b><a></a></root>".SortXml());
    }
}

public static class XmlCompareExtension
{
    public static string SortXml(this string @this)
    {
        var xdoc = XDocument.Parse(@this);

        SortXml(xdoc);

        return xdoc.ToString();
    }

    private static void SortXml(XContainer parent)
    {
        var elements = parent.Elements()
            .OrderBy(e => e.Name.LocalName)
            .ToArray();

        Array.ForEach(elements, e => e.Remove());

        foreach (var element in elements)
        {
            parent.Add(element);
            SortXml(element);
        }
    }
}

0

LBushkin说得对,这并不是一件简单的事情。由于XML是字符串数据,你可以在技术上对内容进行哈希处理并进行比较,但这会受到诸如空格之类的影响。

你可以执行结构化差异(也称为“XML diffgram”)来比较两个文档之间的差异。例如,这就是.NET数据集跟踪更改的方式。

除此之外,你还需要遍历DOM并将元素、属性和值相互比较。如果涉及模式,则还必须考虑位置等因素。


0

我知道这个问题有多老,但我不得不查阅多个来源才找到我要找的答案。以下是使用XNode.DeepEquals但也忽略属性顺序的方法。在经过了13年后,我为了得出这个答案所做的工作量,我想其他人可能会发现这个答案很有帮助。

使用NUnit作为我的测试套件,您可以传入两个XmlDocuments或一个XmlDocument和字符串。这将把XmlDocuments转换为XDocuments,对每个节点进行属性排序,然后执行XNode.DeepEquals()。

Assert.That(XmlHelperService.XMLCompare(xmlDoc, expectedStr), Is.EqualTo(true));
Assert.That(XmlHelperService.XMLCompare(xmlDoc, expectedXmlDoc), Is.EqualTo(true));

代码
public class XmlHelperService
{
    public static bool XMLCompare(XmlDocument primary, string secondaryStr)
    {
        XmlDocument secondary = new XmlDocument();
        secondary.LoadXml(secondaryStr);

        return XMLCompare(primary, secondary);
    }

    public static bool XMLCompare(XmlDocument primary, XmlDocument secondary)
    {
        return XNode.DeepEquals(NormalizeXElement(primary.ToXDocument().Root), NormalizeXElement(secondary.ToXDocument().Root));
    }

    private static XElement NormalizeXElement(XElement element)
    {
        return new XElement(element.Name,
            element.Attributes().OrderBy(x => x.Name.ToString()),
            element.Nodes().Select(n =>
            {
                XElement e = n as XElement;
                if (e != null)
                    return NormalizeXElement(e);
                return n;
            })
        );
    }
}

public static class DocumentExtensions
{
    public static XDocument ToXDocument(this XmlDocument xmlDocument)
    {
        using (var nodeReader = new XmlNodeReader(xmlDocument))
        {
            nodeReader.MoveToContent();
            return XDocument.Load(nodeReader);
        }
    }
}

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