Sorting an XML file based on node value
<p>I was trying to compare two XML files which have same content but on different lines at times. To overcome this, I was trying to sort the XMLs on one of the child nodes (which usually differs in position in both files).</p>

<p>Here is my sample XML file</p>

<pre><code>&lt;Report&gt;
&lt;rptName&gt;Sample&lt;/rptName&gt;
&lt;reportNameGrp&gt;
&lt;grpName&gt;AggrDataSet&lt;/grpName&gt;
&lt;RC&gt;
&lt;rptSubHdr&gt;
&lt;membLglNam&gt;Registered Customer 103&lt;/membLglNam&gt;
&lt;membId&gt;RC103&lt;/membId&gt;
&lt;relCM&gt;CM022&lt;/relCM&gt;
&lt;/rptSubHdr&gt;
&lt;/RC&gt;
&lt;RC&gt;
&lt;rptSubHdr&gt;
&lt;membLglNam&gt;Registered Customer 055&lt;/membLglNam&gt;
&lt;membId&gt;RC055&lt;/membId&gt;
&lt;relCM&gt;CM022&lt;/relCM&gt;
&lt;/rptSubHdr&gt;
&lt;/RC&gt;
&lt;RC&gt;
&lt;rptSubHdr&gt;
&lt;membLglNam&gt;Registered Customer 047&lt;/membLglNam&gt;
&lt;membId&gt;RC047&lt;/membId&gt;
&lt;relCM&gt;CM022&lt;/relCM&gt;
&lt;/rptSubHdr&gt;
&lt;/RC&gt;
&lt;RC&gt;
&lt;rptSubHdr&gt;
&lt;membLglNam&gt;Registered Customer 015&lt;/membLglNam&gt;
&lt;membId&gt;RC015&lt;/membId&gt;
&lt;relCM&gt;CM022&lt;/relCM&gt;
&lt;/rptSubHdr&gt;
&lt;/RC&gt;
&lt;RC&gt;
&lt;rptSubHdr&gt;
&lt;membLglNam&gt;Registered Customer 024&lt;/membLglNam&gt;
&lt;membId&gt;RC024&lt;/membId&gt;
&lt;relCM&gt;CM022&lt;/relCM&gt;
&lt;/rptSubHdr&gt;
&lt;/RC&gt;
&lt;/reportNameGrp&gt;
&lt;/Report&gt;
</code></pre>

<p>I am trying to sort based on <code>&lt;membId&gt;</code> node for <code>&lt;RC&gt;</code> parent node. Whatever method I try, my document fails to sort. I tried using XSLT, but sorting doesn't work. I even tried writing a python script but it fails to sort.</p>

<p>Here is my python script - </p>

<pre><code>import sys
from lxml import etree

filename, tag = sys.argv[1:]

doc = etree.parse(filename, etree.XMLParser(remove_blank_text=True))
root = doc.getroot()
root[:] = sorted(root, key=lambda el: el.findtext(tag))
print etree.tostring(doc, pretty_print=True)
</code></pre>

<p>I execute <code>python test.py 2.xml membId</code> to run the script (please note that 2.xml is file name for input xml and membId is the tag I was looking for).</p>

<p>I will really appreciate any help in where I am going wrong. I am just starting with Python, so I might have made some pretty obvious mistake. A python script or XSLT solution (either) will work for me!</p>

<p>Consider the following XSLT script with Python's <code>lxml</code> integration. Also, you attempt to run a dynamic command line process. Unfortunately, the XSLT will structurally change depending on what specific node you intend to sort. Below will specifically sort <code>&lt;membId&gt;</code> in ascending order:</p>

<p><strong>XSLT</strong></p>

<pre><code>&lt;xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"&gt;
&lt;xsl:output version="1.0" encoding="UTF-8" indent="yes" /&gt;
&lt;xsl:strip-space elements="*"/&gt;

  &lt;!-- Identity Transform --&gt;
  &lt;xsl:template match="@*|node()"&gt;
    &lt;xsl:copy&gt;
      &lt;xsl:apply-templates select="@*|node()"/&gt;
    &lt;/xsl:copy&gt;
  &lt;/xsl:template&gt;  

  &lt;!-- Sort MembId under RC/rptSubHdr --&gt;
  &lt;xsl:template match="reportNameGrp"&gt;
    &lt;xsl:copy&gt;      
        &lt;xsl:copy-of select="grpName"/&gt;
        &lt;xsl:apply-templates select="RC"&gt;          
            &lt;xsl:sort select="rptSubHdr/membId" order="ascending"/&gt;
        &lt;/xsl:apply-templates&gt;
    &lt;/xsl:copy&gt;
  &lt;/xsl:template&gt;

&lt;/xsl:transform&gt;
</code></pre>

<p><strong>Python</strong></p>

<pre><code>import lxml.etree as et

// LOAD XML AND XSL SOURCES
dom = et.parse('Input.xml')
xslt = et.parse('XSLTScript.xsl')

// TRANSFORM
transform = et.XSLT(xslt)
newdom = transform(dom)

// SAVE TO FILE
with open('Output.xml', 'wb') as f:
    f.write(newdom)
</code></pre>

python xml python-2.7 xslt xslt-2.0