什么是比较XML文件相等的最佳方法?

13

我正在使用.NET 2.0,最近的代码更改使我的先前的Assert.AreEqual调用(比较两个XML字符串)无效。新代码库中实际上只有一个XML元素是不同的,因此我的希望是所有其他元素的比较将给我想要的结果。由于它是单元测试的一部分,比较需要以编程方式完成。

起初,我考虑使用几个XmlDocument实例。但后来我发现了这篇文章:http://drowningintechnicaldebt.com/blogs/scottroycraft/archive/2007/05/06/comparing-xml-files.aspx

看起来它可能有效,但我很感兴趣Stack Overflow的反馈是否有更好的方法。

如果可能的话,我想避免添加其他依赖项。

类似的问题


自从这个问题被提出以来,已经有一个更好的答案的重复问题被提出:https://dev59.com/NXA85IYBdhLWcg3wJf8Z#2924439:使用Linq:`XNode.DeepEquals(doc1, doc2)`。 - JohnLBevan
7个回答

12

我通过问题中链接的博客文章发现了这个链接。如果可能的话,我希望避免添加另一个依赖项。 - Scott Lawrence
那么我想你必须决定什么是对你来说的“差异”,并使用XmlDocuments、XpathNavigators等开发自己的算法...但我认为你正在寻找的是“业务差异”,而不是“xml差异”。 - Filini
在这种情况下,我知道两个文档之间的结构将是相同的(没有缺失或额外的元素)。只是元素的值是否相同。 - Scott Lawrence

4

您可能会发现,将XML解析为XmlDocument,并基于XPath查询来进行断言调用,比较不容易出错。以下是我经常使用的一些辅助断言方法。每个方法都需要一个XPathNavigator参数,您可以通过在XmlDocument上调用CreateNavigator()方法或获取文档中检索到的任何节点来获得它。使用示例:

     XmlDocument doc = new XmlDocument( "Testdoc.xml" );
     XPathNavigator nav = doc.CreateNavigator();
     AssertNodeValue( nav, "/root/foo", "foo_val" );
     AssertNodeCount( nav, "/root/bar", 6 )

    private static void AssertNodeValue(XPathNavigator nav,
                                         string xpath, string expected_val)
    {
        XPathNavigator node = nav.SelectSingleNode(xpath, nav);
        Assert.IsNotNull(node, "Node '{0}' not found", xpath);
        Assert.AreEqual( expected_val, node.Value );
    }

    private static void AssertNodeExists(XPathNavigator nav,
                                         string xpath)
    {
        XPathNavigator node = nav.SelectSingleNode(xpath, nav);
        Assert.IsNotNull(node, "Node '{0}' not found", xpath);
    }

    private static void AssertNodeDoesNotExist(XPathNavigator nav,
                                         string xpath)
    {
        XPathNavigator node = nav.SelectSingleNode(xpath, nav);
        Assert.IsNull(node, "Node '{0}' found when it should not exist", xpath);
    }

    private static void AssertNodeCount(XPathNavigator nav, string xpath, int count)
    {
        XPathNodeIterator nodes = nav.Select( xpath, nav );
        Assert.That( nodes.Count, Is.EqualTo( count ) );
    }

谢谢Jeremy,这看起来是一个不错的解决方案。我会试一下的。 - Scott Lawrence

1
我写了一个带有序列化断言的小型库,源代码
示例:
[Test]
public void Foo()
{
   ...
   XmlAssert.Equal(expected, actual, XmlAssertOptions.IgnoreDeclaration | XmlAssertOptions.IgnoreNamespaces);
}

NuGet


1

对一个xml字符串进行简单的字符串比较并不总是有效。为什么?

例如,这两个:

<MyElement></MyElmennt><MyElment/>xml的角度来看是相等的。

有一些算法可以将xml始终转换为相同的样式,它们称为规范化算法。.Net支持规范化。


0

由于XML文件的内容可以有不同的格式,但从DOM的角度来看仍然被视为相同的(在测试相等性时),因此您需要确定相等性的度量标准,例如是否忽略格式?元数据是否被忽略?位置是否重要?还有很多边缘情况。

通常,您会创建一个定义相等规则的类,并将其用于比较。如果您的比较类实现了IEqualityComparer和/或IEqualityComparer<T>接口,则您的类也可以用作内置框架列表中的相等性测试实现。当然,根据您的要求,您可以拥有尽可能多的相等性测量方法。

i.e

IEnumerable<T>.Contains
IEnumerable<T>.Equals
The constructior of a Dictionary etc etc

0
我最终用以下代码得到了想要的结果:
private static void ValidateResult(string validationXml, XPathNodeIterator iterator, params string[] excludedElements)
    {
        while (iterator.MoveNext())
        {
            if (!((IList<string>)excludedElements).Contains(iterator.Current.Name))
            {
                Assert.IsTrue(validationXml.Contains(iterator.Current.Value), "{0} is not the right value for {1}.", iterator.Current.Value, iterator.Current.Name);
            }
        }
    }

在调用该方法之前,我会这样在XmlDocument实例上创建一个导航器:

XPathNavigator nav = xdoc.CreateNavigator();

接下来,我创建一个XPathExpression实例,如下所示:
XPathExpression expression = XPathExpression.Compile("/blah/*");

我在使用表达式创建迭代器后调用该方法:

XPathNodeIterator iterator = nav.Select(expression);

我还在努力想办法进一步优化它,但目前已经能够胜任了。


0
我创建了一个方法来生成简单的XML路径。
static XElement MakeFromXPath(string xpath)
{
    XElement root = null;
    XElement parent = null;
    var splits = xpath.Split('/'); //split xpath into parts
    foreach (var split in splits)
    {
        var el = new XElement(split);
        if (parent != null)
            parent.Add(el);
        else
            root = el; //first element created, set as root
        parent = el;
    }
    return root;
}

示例用法: var element = MakeFromXPath("My/Path/To/Element") element 将包含该值:
<My>
  <Path>
    <To>
      <Element></Element>
    </To>
  </Path>
</My>

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