In C#, what is the best way to sort an XML document alphabetically - both elements and attributes? For example, if I start with b.xml it should become with a.xml:
For now, I'm thinking about recursively traversing all elements and for each element I'll remove its child elements and then add them again in alphabetical order, and do the same for attributes.
Is that the approach I should take or is there a better way or perhaps a ready-made function?
CodePudding user response:
My answer is similar to yours without the need for remove
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication5
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement sortedXml = Sort_XML.sort(doc.Root);
}
}
public class Sort_XML
{
static public XElement sort(XElement root)
{
XElement sortedRoot = new XElement(root.Name.LocalName);
sortRecursive(root, sortedRoot);
return sortedRoot;
}
public static void sortRecursive(XElement parent, XElement sortedRoot)
{
foreach (XElement child in parent.Elements().OrderBy(x => x.Name.LocalName))
{
XElement childElement = new XElement(child.Name.LocalName);
sortedRoot.Add(childElement);
foreach(XAttribute attribute in child.Attributes().OrderBy(x => x.Name.LocalName))
{
childElement.Add(new XAttribute(attribute.Name.LocalName, attribute.Value));
}
if (child.HasElements) sortRecursive(child, childElement);
}
}
}
}
CodePudding user response:
OK, after some experimenting I came up with a function that works on my test file.
private static void SortXml(XmlElement node)
{
// Load the child elements into a collection.
List<XmlElement> childElements = new();
foreach (XmlElement childNode in node.ChildNodes)
{
childElements.Add(childNode);
// Call recursively if the child is not a leaf.
if (childNode.HasChildNodes)
{
SortXml(childNode);
}
}
// Load the attributes into a collection.
List<XmlAttribute> attributes = new();
foreach (XmlAttribute attrib in node.Attributes)
{
attributes.Add(attrib);
}
node.RemoveAll();
// Re-add the child elements (sorted).
foreach (var childNode in childElements.OrderBy(element => element.Name))
{
node.AppendChild(childNode);
}
// Re-add the attributes (sorted).
foreach (var childNode in attributes.OrderBy(attrib => attrib.Name))
{
node.Attributes.Append(childNode);
}
}
I'll test it more thoroughly later.