Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

A Sortable XmlDocument Class

4.88/5 (10 votes)
1 Feb 2009CPOL1 min read 40.8K   324  
Sort the nodes of XmlDocument by many methods easily

Introduction

This class makes XmlDocument class sortable. Actually, I did not search much to see if there is something like this already. I needed it and I wrote it. And, I thought that someone might find this useful.

Background

Basically, this class runs a TAXmlSorter class to sort nodes. TAXmlSorter is an abstract class that implements IComparer<XmlNode> interface. Other comparison classes are based on TAXmlSorter

TAXmlDocument.SortType Enumeration

C#
public enum SortType
{
    Ascending,
    Descending
}

TAXmlDocument.TAXmlSorter Abstract Class

C#
public abstract class TAXmlSorter:IComparer<XmlNode>
{
    Comparison<XmlNode> comparisonDelegate;
    protected TAXmlSorter(SortType st)
    {
        switch (st)
        {
            case SortType.Ascending:
                comparisonDelegate = CompareAsc;
                break;
            case SortType.Descending:
                comparisonDelegate = CompareDesc;
                break;
        }
    }
     protected abstract int CompareAsc(XmlNode x, XmlNode y);
    protected abstract int CompareDesc(XmlNode x, XmlNode y);
     public abstract void InitArray(XmlNode[] array);
     #region IComparer<XmlNode> Members
     int IComparer<XmlNode>.Compare(XmlNode x, XmlNode y)
    {
        return comparisonDelegate(x, y);
    }
     #endregion
}

I used a comparison delegate to better performance. In this way, the application will determine the comparison function on initialize, so it won't check every time if it is an ascending sort.

C#
public sealed class TAXSNodeName : TAXmlSorter 

This class compares node names.

C#
public sealed class TAXSNodeAttribute<AttributeType> : TAXmlSorter
       where AttributeType : IComparable, IConvertible

This class compares attribute of current node.

public sealed class TAXSChildCount : TAXmlSorter

This class compares child node counts.

C#
public sealed class TAXSAttributeCount : TAXmlSorter

This class compares attribute counts.

C#
public sealed class TAXSInnerText : TAXmlSorter

This class compares nodes' inner text values (string comparison).

C#
public sealed class TAXSInnerValue<ValueType> : TAXmlSorter
    where ValueType : IComparable, IConvertible 

This class compares nodes' inner text values (firstly converts to given type).

C#
public sealed class TAXSMulti : TAXmlSorter

This class is a multi comparison class.

Sorting

Actually this class does not use any special sort algorithm. It is based on Array.Sort function.

C#
public void Sort(XmlNode parentNode, TAXmlSorter sorter)
{
    if (parentNode == null)
        throw new ArgumentNullException("parentNode");
    if (sorter == null)
        throw new ArgumentNullException("sorter");
    if (parentNode.OwnerDocument != this)
        throw new Exception(
            "parentNode value is not child of this document");
    //if childcount less than 2 then it is not necessary to sort
    if (parentNode.ChildNodes.Count < 2)
        return;
    //copy the child of parent
    XmlNode[] nodes = new XmlNode[parentNode.ChildNodes.Count];
    for (int i = 0; i < nodes.Length; i++)
        nodes[i] = parentNode.ChildNodes[i];
    
    //call abstract initarray method of sorter
    sorter.InitArray(nodes);
     //call array.sort to sort nodes
    Array.Sort(nodes, sorter);
      //remove all child nodes of parent
    parentNode.InnerXml = "";
     //add sorted nodes
    for (int i = 0; i < nodes.Length; i++)
        parentNode.AppendChild(nodes[i]);
}

Using the Code

XmlFile1.xml Content

XML
<?xml version="1.0" encoding="utf-8" ?>
<root>
  <q>5</q>
  <s/>
  <a/>
  <a id="1">
    <b>
      <c>123</c>
    </b>
  </a>
  <r num="0.1" id="2">false</r>
  <tolgahan id="0" />
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <zoom/>
  <zoom id="-1"/>
</root>

Sort By Node Name

Ascending
C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), new TAXmlDocument.TAXSNodeName() ); 

Output

XML
<?xml version="1.0" encoding="utf-8"?>
<root>
  <a id="1">
    <b>
      <c>
      </c>
    </b>
  </a>
  <a />
  <q>
  </q>
  <r num="0.1" id="2">
  </r>
  <s />
  <tolgahan id="0" />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <zoom id="-1" />
  <zoom />
</root>
Descending
C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), 
new TAXmlDocument.TAXSNodeName( TAXmlDocument.SortType.Descending ));

Output

XML
?<?xml version="1.0" encoding="utf-8"?>
<root>
  <zoom />
  <zoom id="-1" />
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <tolgahan id="0" />
  <s />
  <r num="0.1" id="2">
  </r>
  <q>
  </q>
  <a />
  <a id="1">
    <b>
      <c>
      </c>
    </b>
  </a>
</root>

All comparison classes have SortType (ascending or descending) parameter on constructor.

Sort by Attribute Value (If it is boolean)

C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), 
         new TAXmlDocument.TAXSNodeAttribute<Boolean>( "bool" ));

Output

XML
?<?xml version="1.0" encoding="utf-8"?>
<root>
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <tolgahan id="0" />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <zoom id="-1" />
  <zoom />
  <a />
  <s />
  <q>
  </q>
  <r num="0.1" id="2">
  </r>
  <a id="1">
    <b>
      <c>
      </c>
    </b>
  </a>
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
</root>

Sort by Attribute Value (If it is date)

C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), 
         new TAXmlDocument.TAXSNodeAttribute<DateTime>( "date", 
                           TAXmlDocument.SortType.Descending ));

Output

XML
?<?xml version="1.0" encoding="utf-8"?>
<root>
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <zoom id="-1" />
  <zoom />
  <tolgahan id="0" />
  <s />
  <q>
  </q>
  <a />
  <r num="0.1" id="2">
  </r>
  <a id="1">
    <b>
      <c>
      </c>
    </b>
  </a>
</root>

Sort by ChildCount

C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), 
    new TAXmlDocument.TAXSChildCount());

Output

XML
?<?xml version="1.0" encoding="utf-8"?>
<root>
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <zoom id="-1" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <zoom />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <tolgahan id="0" />
  <s />
  <a />
  <q>5</q>
  <a id="1">
    <b>
      <c>123</c>
    </b>
  </a>
  <r num="0.1" id="2">false</r>
</root>

Sort by Attribute Count

C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), 
    new TAXmlDocument.TAXSAttributeCount());

Output

XML
?<?xml version="1.0" encoding="utf-8"?>
<root>
  <a />
  <zoom />
  <q>5</q>
  <s />
  <zoom id="-1" />
  <tolgahan id="0" />
  <a id="1">
    <b>
      <c>123</c>
    </b>
  </a>
  <r num="0.1" id="2">false</r>
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
</root>

Sort by Inner Value (Try to Sort Integer Value)

C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"), 
    new TAXmlDocument.TAXSInnerValue<Int32>());

Output

XML
<?xml version="1.0" encoding="utf-8"?>
<root>
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <zoom id="-1" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
  <zoom />
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <s />
  <tolgahan id="0" />
  <a />
  <r num="0.1" id="2">false</r>
  <q>5</q>
  <a id="1">
    <b>
      <c>123</c>
    </b>
  </a>
</root>

Multi Sorting 

(Try to sort firstly boolean attribute "bool" desc, then date attribute "date" asc, then inner value):

C#
TAXmlDocument doc = new TAXmlDocument();
doc.Load("c:\\xmlfile1.xml");
doc.Sort(doc.SelectSingleNode("root"),
    new TAXmlDocument.TAXSMulti(
        new TAXmlDocument.TAXSNodeAttribute<Boolean>("bool",
        TAXmlDocument.SortType.Descending),
        new TAXmlDocument.TAXSNodeAttribute<DateTime>("date"),
        new TAXmlDocument.TAXSInnerValue<Int32>()
    )
);

Output

XML
?<?xml version="1.0" encoding="utf-8"?>
<root>
  <tolgahanAlbayrak bool="true" date="01.01.2008 15:13:10" />
  <r num="0.1" id="2">false</r>
  <zoom id="-1" />
  <zoom />
  <s />
  <tolgahan id="0" />
  <a />
  <q>5</q>
  <a id="1">
    <b>
      <c>123</c>
    </b>
  </a>
  <tolgahanAlbayrak bool="false" date="01.01.2008 11:13:10" />
  <tolgahanAlbayrak bool="false" date="01.02.2008 15:13:10" />
</root> 

History

  • 02 Feb 2009: Added sorting operation for child element's one of child value by Andrew Chudley
  • 13 Jan 2009: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)