Q:

Linq - XElement Compare

i am trying to compare two xelements () as below:
XElement parentItems =
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
  <tcm:Item Title="070_Page" Modified="2016-01-06T18:08:36" CP1="-6185, Intro" CP2="-6182, Article Body" CP3="-14507, Article Body1" CP4="-14430, Article Body2" CP5="-14530, Article Body3" CP6="-7064, Article Body4" CP7="-14529, Article Body5" CP8="-7065, Article Body6" CPCount="8" /> 
  <tcm:Item Title="080_Page" Modified="2015-04-23T13:27:59" CP1="-6302, Intro" CP2="-6193, Article Body" CPCount="2" /> 
  <tcm:Item Title="Release Notes" Modified="2016-01-07T21:25:43" CP1="-6303, Release Notes Intro" CP2="-6196, Release Notes Article Body" CPCount="2" />
</tcm:ListItems>

XElement childItems = 
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
  <tcm:Item Title="070_Page" Modified="2016-06-06T19:36:35" CP1="-6185, Intro" CP2="-6147, Media & Delivery Intro" CP3="-6182, Article Body" CP4="-14507, Article Body1" CP5="-14430, Article Body2" CP6="-14530, Article Body3" CP7="-7064, Article Body4" CP8="-14529, Article Body5" CP9="-7065, Article Body6" CPCount="9" /> 
  <tcm:Item Title="080_Page" Modified="2016-02-09T21:03:32" CP1="-6302, Intro" CP2="-6193, Article Body" CPCount="2" /> 
  <tcm:Item Title="Release Notes" Modified="2016-02-09T21:03:33" CP1="-6303, Release Notes Intro" CP2="-6196, Release Notes Article Body" CPCount="2" /> 
  <tcm:Item Title="Release Notes1" Modified="2016-03-09T22:00:13" CP1="-6303, Release Notes Intro" CP2="-6196, Release Notes Article Body" CPCount="2" />
</tcm:ListItems>

I want my result to be (first one has different CPCount, and second one is a new element in childItems):
XElement diff = 
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
  <tcm:Item Title="070_Page" Modified="2016-06-06T19:36:35" CP1="-6185, Intro" CP2="-6147, Media & Delivery Intro" CP3="-6182, Article Body" CP4="-14507, Article Body1" CP5="-14430, Article Body2" CP6="-14530, Article Body3" CP7="-7064, Article Body4" CP8="-14529, Article Body5" CP9="-7065, Article Body6" CPCount="9" />   
  <tcm:Item Title="Release Notes1" Modified="2016-03-09T22:00:13" CP1="-6303, Release Notes Intro" CP2="-6196, Release Notes Article Body" CPCount="2" />
</tcm:ListItems>

I've tried XMLDiff API but no luck. I can loop through and get the results, but sometimes the list can be huge (3000+). Is there a best way to handle this?

A:

Ok, here is the code - in a console app created using VS2015 (could be non-optimal but it works - I get the two expected elements in the end - leaving refactoring for you as a homework):
class Program
{
    //this is just for convenient storage of all collected data
    //also to avoid additional dictionary lookups and processing
    internal class ElementAttributes
    {
        public XElement Element { get; set; }
        public Dictionary<string, string> Attributes;

        public ElementAttributes(XElement element)
        {
            this.Element = element;
            this.Attributes = new Dictionary<string, string>();
        }
    }

    static void Main(string[] args)
    {
        //loading XML elements from embedded resources
        XElement parentItems = XElement.Parse(ResourceXML.parentItems);
        XElement childItems = XElement.Parse(ResourceXML.childItems);

        XElement diff = XElement.Parse(@"<tcm:ListItems xmlns:tcm=""http://www.tridion.com/ContentManager/5.0"" ></tcm:ListItems>");

        var parentDictionary = BuildDictionary(parentItems);
        var childDictionary = BuildDictionary(childItems);

        //perform diff
        foreach (string key in childDictionary.Keys)
        {
            ElementAttributes parentElementAttributes;
            if (parentDictionary.TryGetValue(key, out parentElementAttributes))
            {
                //found Title/key in parent, compare attributes
                foreach (var childAttribute in childDictionary[key].Attributes)
                {
                    var attributeName = childAttribute.Key;
                    var childAttributeValue = childAttribute.Value;

                    string parentAttributeValue;
                    if (parentElementAttributes.Attributes.TryGetValue(attributeName, out parentAttributeValue))
                    {
                        //found attribute in parent, compare value
                        if(childAttributeValue == parentAttributeValue)
                        {
                            //values are equal, compare other attributes
                            continue;
                        }
                    }

                    //parent does not have this attribute OR
                    //different value in child -> show in diff
                    diff.Add(childDictionary[key].Element);

                    //do not compare other attributes
                    break;
                }

                //child may have missing attributes, which are in parent only
                //NOTE: your example does not present a use case for this scenario
                foreach (var parentAttribute in parentElementAttributes.Attributes)
                {
                    string attributeName = parentAttribute.Key;
                    if (!childDictionary[key].Attributes.ContainsKey(attributeName))
                    {
                        //attribute found in parent, but not in child
                        diff.Add(childDictionary[key].Element);
                        break;
                    }
                }
            }
            else
            {
                //not found in parent, show in diff
                diff.Add(childDictionary[key].Element);
            }
        }
    }

    private static Dictionary<string, ElementAttributes> BuildDictionary(XElement element)
    {
        XNamespace tcm = "http://www.tridion.com/ContentManager/5.0";

        var resultDictionary = new Dictionary<string, ElementAttributes>();
        foreach (XElement childElement in element.Elements(tcm + "Item"))
        {
            var attributeDictionary = new ElementAttributes(childElement);
            foreach (XAttribute attribute in childElement.Attributes())
            {
                string[] excludedColumns = {"Title", "Modified"};

                if (excludedColumns.Contains(attribute.Name.LocalName))
                {
                    continue;
                }

                attributeDictionary.Attributes.Add(attribute.Name.LocalName, attribute.Value);
            }
            resultDictionary.Add(childElement.Attribute("Title").Value, attributeDictionary);
        }
        return resultDictionary;
    }
}

