今天早些时候,我的一个同事问我一个问题。他正在为序列化实用程序编写单元测试,需要将生成的xml与预期结果的手写xml文件进行比较。
By eye inspection, the xml seemed to be the same, but just in a different order. The elements were in a different order and the attributes were in a different order. So although order didn’t matter functionally, it eliminated the ability to do:docA.InnerXml.Equals(docB.InnerXml)
经过一番谷歌搜索,我发现那里有一些工具可以进行XML比较,但是它们充斥着太多的选项和功能。我不想学习新知识;我只是想看看xml的内容是否相同(无论顺序如何)。
因此,大约一个小时后,我想出了自己的版本,该版本没有学习曲线,也没有其他选择。它只有一个公共方法:AreEqualNoOrder。
似乎这种事情可能对许多其他人都有用,所以我决定将其发布。附带的控制台应用程序在比较器上运行一些测试,以更好地了解其功能。
using System.Xml;
namespace XMLComparer
{
/// <summary>
/// Compares two xml files for equality of content in any order.
/// </summary>
public class XmlComparer
{
/// <summary>
/// Compares two xml documents for eqality as defined for this comparer.
/// </summary>
/// <param name="docA">The first xml document.</param>
/// <param name="docB">the second xml document.</param>
/// <returns>True if the documents are equal, otherwise false.</returns>
public static bool AreEqualNoOrder(XmlDocument docA, XmlDocument docB)
{
return nodesAreEqualNoOrder(docA.FirstChild, docB.FirstChild);
}
/// <summary>
/// Compares two nodes for equality as defined for this comparer.
/// </summary>
/// <param name="nodeA">A node from the first document.</param>
/// <param name="nodeB">a node from the second document.</param>
/// <returns>True if the nodes are equal, otherwise false.</returns>
private static bool nodesAreEqualNoOrder(XmlNode nodeA, XmlNode nodeB)
{
///////////////
// Compare Text
///////////////
var textA = nodeA.Value;
var textB = nodeB.Value;
if (textA == null || textB == null)
{
// if either is null, then they should both be null.
if (!(textA == null && textB == null))
{
return false;
}
}
else
{
// if they are not null, the text should be the same
if (!textA.Trim().Equals(textB.Trim()))
{
return false;
}
}
/////////////////////
// Compare Attributes
/////////////////////
var attributesA = nodeA.Attributes;
var attributesB = nodeB.Attributes;
if (attributesA == null || attributesB == null)
{
// if either is null, then they should both be null.
if (!(attributesA == null && attributesB == null))
{
return false;
}
}
else
{
// if there are attributes, there should be the same number on A as on B.
if (attributesA.Count != attributesB.Count)
{
return false;
}
// check each attribute and value
for (int i = 0; i < attributesA.Count; i++)
{
var name = attributesA[i].Name;
// if nodeA has an attribute named x, then so should nodeB.
if (attributesB[name] == null)
{
return false;
}
// if nodeA and nodeB both have attributes named x, they should have the same value.
if (attributesB[name].Value != attributesA[name].Value)
{
return false;
}
}
}
//////////////////////
// Compare Child Nodes
//////////////////////
var childsA = nodeA.ChildNodes;
var childsB = nodeB.ChildNodes;
// the number of children of nodeA should be the same as the number of childeren of nodeB.
if (childsA.Count != childsB.Count)
{
return false;
}
// every child of nodeA should have a matching child of nodeB, but not necessarily in the same order.
while (childsA.Count > 0)
{
// look for a match in nodeB for the first child of nodeA
var matchFound = false;
for (int i = 0; i < childsB.Count; i++)
{
if (nodesAreEqualNoOrder(childsA[0], childsB[i]))
{
// if a match is found in nodeB, remove the child in nodeA and the match in nodeB, then move on.
// this is important because if A had child nodes x and x and B had child nodes x and y,
// then both A nodes would match, but B would have a y that wasn't found.
matchFound = true;
childsA[0].ParentNode.RemoveChild(childsA[0]);
childsB[i].ParentNode.RemoveChild(childsB[i]);
break;
}
}
if (!matchFound)
{
// if no match is found, the nodes ain't the same.
return false;
}
}
// if no test failed, the nodes are equal.
return true;
}
}
}
错误的第69行,如果没有B的属性怎么办?更改为读取
整洁的工具,可能会派上用场。并仔细考虑了递归方法。
但是,为什么不以所需的方式编写xml文件,而不是制作一个工具来解决此问题?
谢谢Momerath!立即修复。问题也出现在第35行。
按照我们希望的方式来制作xml的问题在于,如果我们正在测试的生成xml的东西改变了属性的顺序,那并不重要,但是测试会中断并且需要工作。
是的,要发生多少时间...?也许永远不会,但是如果您有比较器,为什么不使用它以防万一?