private static void CompareXmlNodeLists(XmlComparisonRules rules, string pathA, XmlNodeList nodeListA, string pathB, XmlNodeList nodeListB) { if (rules.IsIgnoredPath(pathA)) { return; } if (nodeListA == null && nodeListB == null) { return; } SortedDictionary <string, XmlNode> listA = FilterAndSortNodeList(pathA, pathB, nodeListA); SortedDictionary <string, XmlNode> listB = FilterAndSortNodeList(pathA, pathB, nodeListB); // get all keys SortedSet <string> nodeKeys = new SortedSet <string>(); foreach (var key in listA.Keys) { nodeKeys.Add(key); } foreach (var key in listB.Keys) { nodeKeys.Add(key); } // compare nodes foreach (string nodeKey in nodeKeys) { if (!listA.TryGetValue(nodeKey, out var nodeA)) { continue; } if (!listB.TryGetValue(nodeKey, out var nodeB)) { throw new Exception($"[{pathA}] Emitted node '{nodeKey}' missing!"); } // both exist - compare CompareXmlNodes(rules, pathA + NodeSortKeyOld(nodeA, null) + "/", nodeA, pathB + NodeSortKeyOld(nodeB, null) + "/", nodeB); } }
private static void CompareXmlNodes(XmlComparisonRules rules, string pathA, XmlNode nodeA, string pathB, XmlNode nodeB) { if (rules.IsIgnoredPath(pathA)) { return; } if (rules.IsIgnoredPath(pathB)) { return; } if (nodeA == null && nodeB == null) { return; } if (nodeA == null || nodeB == null) { throw new Exception("[" + pathA + "] (nodeA == null) || (nodeB == null)"); } if (nodeA.GetType() != nodeB.GetType()) { throw new Exception("[" + pathA + "] nodeA.GetType() != nodeB.GetType()"); } // generic node comparison CompareXmlNodeNames(pathA, nodeA, nodeB); CompareXmlNodeTypes(pathA, nodeA, nodeB); CompareXmlAttrColls(rules, pathA + "attr:", nodeA.Attributes, nodeB.Attributes); if (nodeA.HasChildNodes || nodeB.HasChildNodes) { CompareXmlNodeLists(rules, pathA, nodeA.ChildNodes, pathB, nodeB.ChildNodes); } else { // no children - just compare value if (String.Compare(nodeA.Value, nodeB.Value, StringComparison.OrdinalIgnoreCase) != 0) { // values are textually different but could be numerically equal if (BothAreDateTimes(nodeA.Value, nodeB.Value)) { if (!CompareDateTimes(DateTimeStyles.None, nodeA.Value, nodeB.Value) && !CompareDateTimes(DateTimeStyles.AssumeLocal, nodeA.Value, nodeB.Value) && !CompareDateTimes(DateTimeStyles.AssumeUniversal, nodeA.Value, nodeB.Value) && !CompareDateTimes(DateTimeStyles.NoCurrentDateDefault, nodeA.Value, nodeB.Value) && !CompareWithDateTimeZoneParser(nodeA.Value, nodeB.Value, false) && !CompareWithDateTimeZoneParser(nodeA.Value, nodeB.Value, true) ) { // date/times are not equal! throw new Exception($"[{pathA}] NodeValue (as DateTime) '{nodeA.Value}' != '{nodeB.Value}'"); } } else if (DateTimeOffset.TryParse(nodeA.Value, out var dtoA) && DateTimeOffset.TryParse(nodeB.Value, out var dtoB)) { if (dtoA != dtoB) { throw new Exception($"[{pathA}] NodeValue (as DateTimeOffset) '{dtoA}' != '{dtoB}'"); } } else if (Int32.TryParse(nodeA.Value, out var intA) && Int32.TryParse(nodeB.Value, out var intB)) { if (intA != intB) { throw new Exception($"[{pathA}] NodeValue (as Int32) '{intA}' != '{intB}'"); } } else if (Decimal.TryParse(nodeA.Value, out var numA) && Decimal.TryParse(nodeB.Value, out var numB)) { if (Math.Round(numA, 12) != Math.Round(numB, 12)) { throw new Exception($"[{pathA}] NodeValue (as Decimal) '{numA}' != '{numB}'"); } } else { throw new Exception($"[{pathA}] NodeValue '{nodeA.Value}' != '{nodeB.Value}'"); } } } // specific node type comparisons if (nodeA.GetType() == typeof(XmlElement)) { //CompareXmlElements(ignorePaths, path + "elt:" + nodeA.Name, nodeA as XmlElement, nodeB as XmlElement); } else if (nodeA.GetType() == typeof(XmlDeclaration)) { //CompareXmlDeclarations(ignorePaths, path + "decl:" + nodeA.Name, nodeA as XmlDeclaration, nodeB as XmlDeclaration); } else if (nodeA.GetType() == typeof(XmlText)) { CompareXmlTexts(rules, pathA + "text:" + nodeA.Name, nodeA as XmlText, nodeB as XmlText); } else { throw new NotImplementedException("[" + pathA + "] NodeType: " + nodeA.GetType().Name); } }