你如何比较两个XML文档?

作为一些广泛的unit testing的基类的一部分,我正在编写一个帮助函数recursion地将一个XmlDocument对象的节点与C#(.NET)中的另一个进行比较。 这个的一些要求:

  • 第一个文档是文件,例如我想让XML文档看起来像什么。 因此,第二个是我想要find差异的一个,它不得包含不在第一个文档中的额外节点。
  • 当发现太多明显的差异时,必须抛出一个exception,而且应该很容易理解。
  • 子元素顺序很重要,属性可以以任何顺序。
  • 有些属性是可以忽略的; 特别是xsi:schemaLocationxmlns:xsi ,尽pipe我希望能够通过哪些。
  • 名称空间的前缀必须在属性和元素中匹配。
  • 元素之间的空白不相关。
  • 元素将具有子元素 InnerText ,但不是两者。

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

在旁边,你会把第一和第二个文件称为什么? 我一直把它们称为“源代码”和“目标”,但是由于源代码是我想要的目标 ,所以感觉不对,否则我会抛出一个exception。

微软有一个可以使用的XML差异API

尝试XMLUnit 。 这个库可用于Java和.Net

另一种做法是 –

  1. 将这两个文件的内容分成两个不同的string。
  2. 使用XSLT转换string(这将只复制到两个新的string)。 这将确保元素外的所有空间都被移除。 这将导致两个新的string。
  3. 现在,比较两个string。

这不会给你的差异的确切位置,但如果你只是想知道是否有差异,这是很容易做到没有任何第三方库。

比较XML文档是复杂的。 谷歌for xmldiff(甚至有一个微软解决scheme)的一些工具。 我已经解决了这个问题。 我使用XSLT对元素和属性进行sorting(因为有时它们会以不同的顺序出现,而我不在乎),并过滤掉我不想比较的属性,然后使用XML :: Diff或者XML :: SemanticDiff perl模块,或者将每个文档的每个元素和属性打印在一个单独的行上,然后在结果中使用Unix命令行diff。

这段代码并不能满足你所​​有的要求,但是这很简单,我正在使用我的unit testing。 属性顺序并不重要,但是元素顺序确实如此。 元素内部文本不进行比较。 比较属性时,我也忽略了大小写,但可以轻松地删除它。

 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 (i = 0; i <= primary.Elements.Count - 1; i++) { if (XMLCompare(primary.Nodes(i), secondary.Nodes(i)) == false) return false; } } return true; } 

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

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

它是这里描述的X-Diffalgorithm的C#端口: http : //pages.cs.wisc.edu/~yuanwang/xdiff.html

免责声明 :我写了:)

与OP没有关系,因为它目前忽略子顺序,但是如果你想要一个只有代码的解决scheme,你可以尝试XmlSpecificationCompare ,我有点误导了开发。

Based @Two Cents回答并使用这个链接XMLSorting我创build了自己的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; } 

sortingXML程序

  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; } } } }