I have a XDocument where I\'d like to sort all of the elements alphabetically. Here\'s a simplified version of the structure:
Here is an updated example that will include all attributes when performing the sort.
private static XElement Sort(XElement element)
{
XElement newElement = new XElement(element.Name,
from child in element.Elements()
orderby child.Name.ToString()
select Sort(child));
if (element.HasAttributes)
{
foreach (XAttribute attrib in element.Attributes())
{
newElement.SetAttributeValue(attrib.Name, attrib.Value);
}
}
return newElement;
}
private static XDocument Sort(XDocument file)
{
return new XDocument(Sort(file.Root));
}
This post helped me a lot, because I did not want to perform an XML sort using XSLT since I did not want to reformat the XML. I searched all around for a simple solution to perform an XML sort using C# and ASP.NET and I was delighted when I found this thread. Thanks to all, this did exactly what I needed.
You already have a method to sort the elements. Just apply it recursively:
private static XElement Sort(XElement element)
{
return new XElement(element.Name,
from child in element.Elements()
orderby child.Name.ToString()
select Sort(child));
}
private static XDocument Sort(XDocument file)
{
return new XDocument(Sort(file.Root));
}
Note that this strips all non-element nodes (attributes, text, comments, etc.) from your document.
If you want to keep the non-element nodes, you have to copy them over:
private static XElement Sort(XElement element)
{
return new XElement(element.Name,
element.Attributes(),
from child in element.Nodes()
where child.NodeType != XmlNodeType.Element
select child,
from child in element.Elements()
orderby child.Name.ToString()
select Sort(child));
}
private static XDocument Sort(XDocument file)
{
return new XDocument(
file.Declaration,
from child in file.Nodes()
where child.NodeType != XmlNodeType.Element
select child,
Sort(file.Root));
}
I think these extension method work best.
public static class XmlLinq
{
public static void Sort(this XElement source, bool sortAttributes = true)
{
if (source == null)
throw new ArgumentNullException("source");
if (sortAttributes)
source.SortAttributes();
List<XElement> sortedChildren = source.Elements().OrderBy(e => e.Name.ToString()).ToList();
source.RemoveNodes();
sortedChildren.ForEach(c => source.Add(c));
sortedChildren.ForEach(c => c.Sort());
}
public static void SortAttributes(this XElement source)
{
if (source == null)
throw new ArgumentNullException("source");
List<XAttribute> sortedAttributes = source.Attributes().OrderBy(a => a.ToString()).ToList();
sortedAttributes.ForEach(a => a.Remove());
sortedAttributes.ForEach(a => source.Add(a));
}
}
THIS METHOD MAKES A REAL DOCUMENT EXTENSION AND PRESERVES THE ATTRIBUTES AND TEXT VALUES
I came up with this based off a few different posts and code from here and there... Thanks to all who contributed!
Within the same namespace (not the same class) add the following...
public static void Sort(this XElement source, bool bSortAttributes = true)
{
//Make sure there is a valid source
if (source == null) throw new ArgumentNullException("source");
//Sort attributes if needed
if (bSortAttributes)
{
List<XAttribute> sortedAttributes = source.Attributes().OrderBy(a => a.ToString()).ToList();
sortedAttributes.ForEach(a => a.Remove());
sortedAttributes.ForEach(a => source.Add(a));
}
//Sort the children IF any exist
List<XElement> sortedChildren = source.Elements().OrderBy(e => e.Name.ToString()).ToList();
if (source.HasElements)
{
source.RemoveNodes();
sortedChildren.ForEach(c => c.Sort(bSortAttributes));
sortedChildren.ForEach(c => source.Add(c));
}
}
To use the document extension...
//Load the xDoc
XDocument xDoc = XDocument.Load("c:\test.xml");
//Sort the root element
xDoc.Root.Sort();