I needed to sort an xml file based on an attribute rather than a tag value. This proved to be a little more subtle problem than I first imagined. I've worked up a simple example of what is required to sort an xml file on an attribute value:
// FYI, there is a build event that copies the item dictionary to the correct
// directory. If you get a run time error because the dictionary is missing,
// you need to check the output of the build event.
using System;
using System.Xml;
using System.Xml.XPath;
namespace XMLSort
{
class Program
{
static void Main(string [ ] args)
{
// Create an xml doc instance and load the item dictionary
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(@"ItemDictionary.xml");
// Creeat the sorted document place holder
XmlDocument xmlSortedDoc = new XmlDocument();
// Copy the dictionary structure by loading the outer xml from doc
xmlSortedDoc.LoadXml(xmlDoc.OuterXml);
// remove the unsorted nodes
xmlSortedDoc.SelectSingleNode("//items").RemoveAll();
// Select the items node
XmlNode node = xmlDoc.SelectSingleNode("//items");
// Create an xpath navigator object
XPathNavigator navigator = node.CreateNavigator();
// Now the fun begins. We want to navigate the item nodes.
// There are four nodes in the itemdictionary.xml file.
XPathExpression selectExpression = navigator.Compile("item");
// Finally! We get to define the sort conditions.
// In this case, I want to sort on the name attribute of the item tag.
selectExpression.AddSort("@name", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Text);
// We need to create an iterator to loop through the nodes.
// This allows us to create the inner xml item elements for
// the sorted document
XPathNodeIterator nodeIterator = navigator.Select(selectExpression);
// Loop through the iterator, adding the nodes to the sorted document
while ( nodeIterator.MoveNext() )
{
// Clone the iterator so we do not lose position
XPathNavigator clone = nodeIterator.Current.Clone();
// Try to move to name attribute
if ( clone.MoveToFirstAttribute() )
{
// Select the node from the xml document
XmlNode linkNode = xmlDoc.SelectSingleNode("//item[@name=\"" + clone.Value + "\"]");
// Copy the node by importing it to a temporary node
XmlNode importedLinkNode = xmlSortedDoc.ImportNode(linkNode, true);
// Select the items node, then append the imported node as a child
xmlSortedDoc.SelectSingleNode("//items").AppendChild(importedLinkNode);
}
else
{
Console.WriteLine("Skipping node.");
}
}
// Save the sorted dictionary to a new file
xmlSortedDoc.Save(@"SortedItemDictionary.xml");
}
}
}
Here's the VS 2010 project, which includes the itemdictionary.xml file used in the example: