你如何比较两个XML文档?
作为一些广泛的unit testing的基类的一部分,我正在编写一个帮助函数recursion地将一个XmlDocument对象的节点与C#(.NET)中的另一个进行比较。 这个的一些要求:
- 第一个文档是源文件,例如我想让XML文档看起来像什么。 因此,第二个是我想要find差异的一个,它不得包含不在第一个文档中的额外节点。
- 当发现太多明显的差异时,必须抛出一个exception,而且应该很容易理解。
- 子元素顺序很重要,属性可以以任何顺序。
- 有些属性是可以忽略的; 特别是
xsi:schemaLocation
和xmlns:xsi
,尽pipe我希望能够通过哪些。 - 名称空间的前缀必须在属性和元素中匹配。
- 元素之间的空白不相关。
- 元素将具有子元素或
InnerText
,但不是两者。
当我把一些东西拼凑在一起的时候: 有没有人写过这样的代码,可以在这里分享吗?
在旁边,你会把第一和第二个文件称为什么? 我一直把它们称为“源代码”和“目标”,但是由于源代码是我想要的目标 ,所以感觉不对,否则我会抛出一个exception。
微软有一个可以使用的XML差异API
尝试XMLUnit 。 这个库可用于Java和.Net
我今天search了一个更完整的问题解决scheme列表,我将尽快尝试其中的一个:
- http://xmlunit.sourceforge.net/
- http://msdn.microsoft.com/en-us/library/aa302294.aspx
- http://jolt.codeplex.com/wikipage?title=Jolt.Testing.Assertions.XML.Adaptors
- http://www.codethinked.com/checking-xml-for-semantic-equivalence-in-c
- https://vkreynin.wordpress.com/tag/xml/
- http://gandrusz.blogspot.com/2008/07/recently-i-have-run-into-usual-problem.html
- http://xmlspecificationcompare.codeplex.com/
- https://github.com/netbike/netbike.xmlunit
另一种做法是 –
- 将这两个文件的内容分成两个不同的string。
- 使用XSLT转换string(这将只复制到两个新的string)。 这将确保元素外的所有空间都被移除。 这将导致两个新的string。
- 现在,比较两个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; } } } }
- 返回IQueryable <T>或不返回IQueryable <T>
- .NET中的小数不准确
- 编译dynamicexpression式所需的一个或多个types无法find。 您是否缺less对Microsoft.CSharp.dll和System.Core.dll的引用?
- 远程服务器返回错误:(407)需要代理validation
- ASP.NET MVC – 如何在login页面显示未经授权的错误?
- basicHttpBinding vs wsHttpBinding
- 使用LINQsearch树
- 你能从一个MethodInfo对象得到一个Func <T>(或类似的)吗?
- C# – 创build一个Process.Start等到进程启动