I have a fairly complex use case where I am trying to modify an XDocument based on the following conditions:
1. GroupBy all descendants on a primary key
2. Combine the XElements in each IGrouping together and select only Distinct elements based on a custom equality comparer
3. Replace the last XElement for each IGrouping in the original XDocument with the concatenated and distinct results, and delete the other XElements from the original XDocument
My attempt at doing this is as follows:
IEnumerable<IGrouping<string, XElement>> groups = doc.Root.Descendants() .GroupBy(_ => _.GetAbsoluteXPath()) .OrderByDescending(_ => _.First().Parent.Ancestors().Count()) .Select(_ => _); foreach (var group in groups) { List<XElement> merge = new List<XElement>(); merge.Add(group.Last()); foreach (var el in group.Reverse().Skip(1)) { merge.Add(new XElement(el)); if (el.Parent != null) { if (el.Parent.Elements().Count() == 1) { el.Parent.SetAttributeValue("operation", "remove"); } el.Remove(); } } group.Last().ReplaceWith(merge.Distinct(new XElementEqualityComparer())); } doc.Root.Descendants() .Where(_ => _.Attribute("operation") != null && String.Equals(_.Attribute("operation").Value, "remove")) .Remove();
However I am having a few issues that I can't figure out how to resolve and wonder if there's a more efficient way of accomplishing my goals:
1. Can I guarantee that group.Last() will always result in the last matching XElement in the original XDocument? In other words, will my GroupBy/OrderBy combination attempt to preserve the original document order wherever possible?
2. I want parent XElement instances to be removed from the original XDocument if the call to el.Remove() causes the parent of el to become empty. My solution has a clunky workaround to accomplish this, but I can't help but feel there must be a better way.
Thank you for your help.