作为一些广泛的单元测试的基类的一部分,我正在编写一个辅助函数,它在C#(.NET)中递归地将一个XmlDocument对象的节点与另一个XmlDocument对象的节点进行比较。一些要求:
xsi:schemaLocation
和xmlns:xsi
,虽然我希望能够传递哪些是。InnerText
,但不是两者。虽然我正在废弃一些东西:有没有人写过这样的代码,是否有可能在这里分享?
顺便说一下,你会把第一个和第二个文件称为什么?我一直把它们称为“源”和“目标”,但它感觉不对,因为源是我希望目标看起来像,否则我抛出异常。
微软有一个你可以使用的XML diff API
为了比较自动化测试中的两个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
基于@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;
}
}
}
}
我编写了这个基于XSLT 1.0的比较表,您可以将b.xml与输入xml输出的输出中的项目差异进行比较,这些项目不在b.xml中。
今天我搜索了一个更完整的问题解决方案列表,我将很快尝试其中一个:
试试XMLUnit。该库可用于Java和.Net
比较XML文档很复杂。谷歌为xmldiff(甚至是微软的解决方案)提供一些工具。我已经通过几种方式解决了这个问题。我使用XSLT对元素和属性进行排序(因为有时它们会以不同的顺序出现,我不关心它),并过滤掉我不想比较的属性,然后使用XML::Diff或XML::SemanticDiff perl模块,或者将每个元素和属性打印在一个单独的行上,并在结果上使用Unix命令行diff。
此代码不能满足您的所有要求,但它很简单,我正在使用我的单元测试。属性顺序无关紧要,但元素顺序确实如此。元素内部文本未进行比较。在比较属性时我也忽略了大小写,但您可以轻松删除它。
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;
}
我使用ExamXML来比较XML文件。你可以尝试一下。作者A7Soft还提供用于比较XML文件的API
https://github.com/CameronWills/FatAntelope Microsoft XML Diff API的另一个替代库。它有一个XML diffing算法,可以对两个XML文档进行无序比较,并产生最佳匹配。
它是这里描述的X-Diff算法的C#端口:http://pages.cs.wisc.edu/~yuanwang/xdiff.html
免责声明:我写了:)
另一种方法是 -
这不会为您提供差异的确切位置,但如果您只是想知道是否存在差异,那么在没有任何第三方库的情况下这很容易实现。
与OP无关,因为它目前忽略了子命令,但是如果你想要一个只有代码的解决方案,你可以尝试我XmlSpecificationCompare开发的somewhat misguidedly。