Dot-Net
比較 XElement 對象的最佳方法
在單元測試中,我將一個
XElement對象與我期望的對象進行比較。.ToString()我使用的方法是呼叫XElement對象並將其與硬編碼的字元串值進行比較。這種方法結果很不舒服,因為我總是要注意字元串中的格式。我檢查了 XElement.DeepEquals() 方法,但出於任何原因它沒有幫助。
有誰知道我應該使用什麼最好的方法?
我發現這篇優秀的文章很有用。它包含一個程式碼範例,該範例實現了在比較之前規範化 XML 樹的替代方法
XNode.DeepEquals,這使得非語義內容無關緊要。為了說明,
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, NormalizeAttributes(element, havePSVI), (DateTime)element); case XmlTypeCode.Decimal: return new XElement(element.Name, NormalizeAttributes(element, havePSVI), (decimal)element); case XmlTypeCode.Double: return new XElement(element.Name, NormalizeAttributes(element, havePSVI), (double)element); case XmlTypeCode.Float: return new XElement(element.Name, NormalizeAttributes(element, havePSVI), (float)element); case XmlTypeCode.HexBinary: case XmlTypeCode.Language: return new XElement(element.Name, NormalizeAttributes(element, havePSVI), ((string)element).ToLower()); default: return new XElement(element.Name, NormalizeAttributes(element, havePSVI), element.Nodes().Select(n => NormalizeNode(n, havePSVI)) ); } } else { return new XElement(element.Name, NormalizeAttributes(element, havePSVI), element.Nodes().Select(n => NormalizeNode(n, havePSVI)) ); } } }