public void Compare(Stream stream1, Stream stream2, IXmlCompareHandler callback) { var loadOptions = LoadOptions.SetBaseUri | LoadOptions.SetLineInfo; const int leftId = 1; const int rightId = 2; var doc1 = XDocument.Load(stream1, loadOptions); var doc2 = XDocument.Load(stream2, loadOptions); var nsm1 = new XmlNamespaceManagerEnhanced(doc1); var nsm2 = new XmlNamespaceManagerEnhanced(doc2); var doc1Descendants = doc1.Descendants() .Where(a => a != doc1.Root) .Select(a => new { Source = leftId, Element = a }); var doc2Descendants = doc2.Descendants() .Where(a => a != doc2.Root) .Select(a => new { Source = rightId, Element = a }); var doc1DescendantsDiff = doc1Descendants .Where(a => !doc2Descendants.Any(a2 => ElementsAreEqual(a2.Element, a.Element))) .Select(a => Elem.Create(a.Element, a.Source)) .ToList(); var doc2DescendantsDiff = doc2Descendants .Where(a => !doc1Descendants.Any(a1 => ElementsAreEqual(a1.Element, a.Element))) .Select(a => Elem.Create(a.Element, a.Source)) .ToList(); var itemsToProcess = doc1DescendantsDiff .Concat(doc2DescendantsDiff) .OrderBy(a => a.LineNumber).ThenBy(a => a.Source) .ToList(); var xPathsAdded = new List <string>(); var xPathsRemoved = new List <string>(); var xPathsChanged = new List <string>(); var additions = new List <ElementAddedEventArgs>(); var changes = new List <ElementChangedEventArgs>(); var removals = new List <ElementRemovedEventArgs>(); foreach (var item in itemsToProcess) { if (xPathsAdded.Any(a => a == item.XPath) || xPathsChanged.Any(a => a == item.XPath) || xPathsRemoved.Any(a => a == item.XPath) ) { continue; } //Console.WriteLine(item.XPath); var node1 = item.Source != leftId ? default(XElement) : item.Element; var node2 = item.Source != rightId ? default(XElement) : item.Element; // now get item from other side switch (item.Source) { case leftId: { // get node2 var e = itemsToProcess.FirstOrDefault(a => a.Source == rightId && a.XPath == item.XPath); if (e != null) { node2 = e.Element; } break; } case rightId: { // get node1 var e = itemsToProcess.FirstOrDefault(a => a.Source == leftId && a.XPath == item.XPath); if (e != null) { node1 = e.Element; } break; } default: { throw new Exception("Invalid Source " + item + " for item : " + item.XPath); } } if (node1 != null && node2 != null) { CompareAttributes(node1, node2, callback); // if there are sub-elements, those will be handled separately if (node1.HasElements || node2.HasElements) { continue; } } if (node1 == null && node2 != null) { //added if (xPathsAdded.Any(a => item.XPath.StartsWith(a))) { // if the node's parent exists in the list of items, // there is no need to call the callback continue; } xPathsAdded.Add(item.XPath); additions.Add(new ElementAddedEventArgs(item.XPath, node2, node2.LineNumber())); continue; } if (node1 != null && node2 == null) { //removed if (xPathsRemoved.Any(a => item.XPath.StartsWith(a))) { // if the node's parent exists in the list of items, // there is no need to call the callback continue; } xPathsRemoved.Add(item.XPath); removals.Add(new ElementRemovedEventArgs(item.XPath, node1, node1.LineNumber())); continue; } if (node1 != null && node2 != null) { //might have changed //compare values if (xPathsChanged.Any(a => item.XPath.StartsWith(a))) { // if the node's parent exists in the list of items, // there is no need to call the callback continue; } xPathsChanged.Add(item.XPath); var val1 = node1.Value; var val2 = node2.Value; if (string.Equals(val1, val2)) { continue; } changes.Add(new ElementChangedEventArgs(item.XPath, node1, node1.LineNumber(), node2, node2.LineNumber())); continue; } throw new Exception("Invalid scenario while comparing elements: " + item.XPath); } foreach (var item in removals) { callback.ElementRemoved(item); } foreach (var item in additions) { callback.ElementAdded(item); } foreach (var item in changes) { callback.ElementChanged(item); } }
public void Compare(Stream stream1, Stream stream2, IXmlCompareHandler callback) { var loadOptions = LoadOptions.SetBaseUri | LoadOptions.SetLineInfo; const int leftId = 1; const int rightId = 2; var doc1 = XDocument.Load(stream1, loadOptions); var doc2 = XDocument.Load(stream2, loadOptions); var nsm1 = new XmlNamespaceManagerEnhanced(doc1); var nsm2 = new XmlNamespaceManagerEnhanced(doc2); var doc1Descendants = doc1.Descendants() .Where(a => a != doc1.Root) .Select(a => new { Source = leftId, Element = a }); var doc2Descendants = doc2.Descendants() .Where(a => a != doc2.Root) .Select(a => new { Source = rightId, Element = a }); var doc1DescendantsDiff = doc1Descendants .Where(a => !doc2Descendants.Any(a2 => ElementsAreEqual(a2.Element, a.Element))) .Select(a => Elem.Create(a.Element, a.Source)) .ToList(); var doc2DescendantsDiff = doc2Descendants .Where(a => !doc1Descendants.Any(a1 => ElementsAreEqual(a1.Element, a.Element))) .Select(a => Elem.Create(a.Element, a.Source)) .ToList(); var itemsToProcess = doc1DescendantsDiff .Concat(doc2DescendantsDiff) .OrderBy(a => a.LineNumber).ThenBy(a => a.Source) .ToList(); var xPathsAdded = new List<string>(); var xPathsRemoved = new List<string>(); var xPathsChanged = new List<string>(); var additions = new List<ElementAddedEventArgs>(); var changes = new List<ElementChangedEventArgs>(); var removals = new List<ElementRemovedEventArgs>(); foreach (var item in itemsToProcess) { if (xPathsAdded.Any(a => a == item.XPath) || xPathsChanged.Any(a => a == item.XPath) || xPathsRemoved.Any(a => a == item.XPath) ) continue; //Console.WriteLine(item.XPath); var node1 = item.Source != leftId ? default(XElement) : item.Element; var node2 = item.Source != rightId ? default(XElement) : item.Element; // now get item from other side switch (item.Source) { case leftId: { // get node2 var e = itemsToProcess.FirstOrDefault(a => a.Source == rightId && a.XPath == item.XPath); if (e != null) { node2 = e.Element; } break; } case rightId: { // get node1 var e = itemsToProcess.FirstOrDefault(a => a.Source == leftId && a.XPath == item.XPath); if (e != null) { node1 = e.Element; } break; } default: { throw new Exception("Invalid Source " + item + " for item : " + item.XPath); } } if (node1 != null && node2 != null) { CompareAttributes(node1, node2, callback); // if there are sub-elements, those will be handled separately if (node1.HasElements || node2.HasElements) continue; } if (node1 == null && node2 != null) { //added if (xPathsAdded.Any(a => item.XPath.StartsWith(a))) { // if the node's parent exists in the list of items, // there is no need to call the callback continue; } xPathsAdded.Add(item.XPath); additions.Add(new ElementAddedEventArgs(item.XPath, node2, node2.LineNumber())); continue; } if (node1 != null && node2 == null) { //removed if (xPathsRemoved.Any(a => item.XPath.StartsWith(a))) { // if the node's parent exists in the list of items, // there is no need to call the callback continue; } xPathsRemoved.Add(item.XPath); removals.Add(new ElementRemovedEventArgs(item.XPath, node1, node1.LineNumber())); continue; } if (node1 != null && node2 != null) { //might have changed //compare values if (xPathsChanged.Any(a => item.XPath.StartsWith(a))) { // if the node's parent exists in the list of items, // there is no need to call the callback continue; } xPathsChanged.Add(item.XPath); var val1 = node1.Value; var val2 = node2.Value; if (string.Equals(val1, val2)) continue; changes.Add(new ElementChangedEventArgs(item.XPath, node1, node1.LineNumber(), node2, node2.LineNumber())); continue; } throw new Exception("Invalid scenario while comparing elements: " + item.XPath); } foreach (var item in removals) { callback.ElementRemoved(item); } foreach (var item in additions) { callback.ElementAdded(item); } foreach (var item in changes) { callback.ElementChanged(item); } }