在单元测试中,我将XElement对象与我预期的对象进行比较.我使用的方法是在XElement对象上调用.ToString(),并将其与硬编码的字符串值进行比较.这种方法原来是非常不舒服,因为我总是要注意字符串中的格式.
我检查了XElement.DeepEquals()方法,但由于任何原因,它没有帮助.
有人有想法我应该使用什么最好的方法?
我发现
this excellent article有用.它包含一个代码示例,实现了XNode.DeepEquals的替代方法,在比较之前规范了XML树,这使得非语义内容无关紧要.
为了说明,对于这些语义上等效的文档,XNode.DeepEquals的实现返回false:
XElement root1 = XElement.Parse("<Root a='1' b='2'><Child>1</Child></Root>"); XElement root2 = XElement.Parse("<Root b='2' a='1'><Child>1</Child></Root>");
然而,从文章中使用DeepEqualsWithNormalization的实现,您将得到值为true,因为属性的顺序不被认为是重要的.这个实现包括在下面.
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; using System.Xml.Schema; public static class MyExtensions { public static string ToStringAlignAttributes(this XDocument document) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; settings.NewLineOnAttributes = true; StringBuilder stringBuilder = new StringBuilder(); using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder,settings)) document.WriteTo(xmlWriter); return stringBuilder.ToString(); } } class Program { private static class Xsi { public static XNamespace xsi = “http://www.w3.org/2001/XMLSchema-instance”; public static XName schemaLocation = xsi + “schemaLocation”; public static XName noNamespaceSchemaLocation = xsi + “noNamespaceSchemaLocation”; } public static XDocument Normalize(XDocument source,XmlSchemaSet schema) { bool havePSVI = false; // validate,throw errors,add PSVI information if (schema != null) { source.Validate(schema,null,true); havePSVI = true; } return new XDocument( source.Declaration,source.Nodes().Select(n => { // Remove comments,processing instructions,and text nodes that are // children of XDocument. Only white space text nodes are allowed as // children of a document,so we can remove all text nodes. if (n is XComment || n is XProcessingInstruction || n is XText) return null; XElement e = n as XElement; if (e != null) return NormalizeElement(e,havePSVI); return n; } ) ); } public static bool DeepEqualsWithNormalization(XDocument doc1,XDocument doc2,XmlSchemaSet schemaSet) { XDocument d1 = Normalize(doc1,schemaSet); XDocument d2 = Normalize(doc2,schemaSet); return XNode.DeepEquals(d1,d2); } private static IEnumerable<XAttribute> NormalizeAttributes(XElement element,bool havePSVI) { return element.Attributes() .Where(a => !a.IsNamespaceDeclaration && a.Name != Xsi.schemaLocation && a.Name != Xsi.noNamespaceSchemaLocation) .OrderBy(a => a.Name.NamespaceName) .ThenBy(a => a.Name.LocalName) .Select( a => { if (havePSVI) { var dt = a.GetSchemaInfo().SchemaType.TypeCode; switch (dt) { case XmlTypeCode.Boolean: return new XAttribute(a.Name,(bool)a); case XmlTypeCode.DateTime: return new XAttribute(a.Name,(DateTime)a); case XmlTypeCode.Decimal: return new XAttribute(a.Name,(decimal)a); case XmlTypeCode.Double: return new XAttribute(a.Name,(double)a); case XmlTypeCode.Float: return new XAttribute(a.Name,(float)a); case XmlTypeCode.HexBinary: case XmlTypeCode.Language: return new XAttribute(a.Name,((string)a).ToLower()); } } return a; } ); } private static XNode NormalizeNode(XNode node,bool havePSVI) { // trim comments and processing instructions from normalized tree if (node is XComment || node is XProcessingInstruction) return null; XElement e = node as XElement; if (e != null) return NormalizeElement(e,havePSVI); // Only thing left is XCData and XText,so clone them return node; } private static XElement NormalizeElement(XElement element,bool havePSVI) { if (havePSVI) { var dt = element.GetSchemaInfo(); switch (dt.SchemaType.TypeCode) { case XmlTypeCode.Boolean: return new XElement(element.Name,NormalizeAttributes(element,havePSVI),(bool)element); case XmlTypeCode.DateTime: return new XElement(element.Name,(DateTime)element); case XmlTypeCode.Decimal: return new XElement(element.Name,(decimal)element); case XmlTypeCode.Double: return new XElement(element.Name,(double)element); case XmlTypeCode.Float: return new XElement(element.Name,(float)element); case XmlTypeCode.HexBinary: case XmlTypeCode.Language: return new XElement(element.Name,((string)element).ToLower()); default: return new XElement(element.Name,element.Nodes().Select(n => NormalizeNode(n,havePSVI)) ); } } else { return new XElement(element.Name,havePSVI)) ); } } }