3

I have a large xml file with a lot of row nodes. I want to sort it by each row's id value.

So this would be an example input:

<database>
  <table>
    <row>
      <id>10</id>
      <foo>bar</foo>
    </row>
    <row>
      <id>5</id>
      <foo>poit</foo>
    </row>
    <row>
      <id>1</id>
      <foo>narf</foo>
    </row>
  </table>
</database>

and this the expected output:

<database>
  <table>
    <row>
      <id>1</id>
      <foo>narf</foo>
    </row>
    <row>
      <id>5</id>
      <foo>poit</foo>
    </row>
    <row>
      <id>10</id>
      <foo>bar</foo>
    </row>
  </table>
</database>

How can I achieve that? I have xmlstarlet at my disposal. It features a transform and appearantly I can provide a xslt stylecheet in a xsl file.

I haven't worked with xslt before and am unsure how to proceed.

I have found some related sorting questions providing some XSLT examples, yet I could not get them working in my use case.

My current sort.xslt (Note: I don't know what I am doing) looks like:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="@*">
    <xsl:sort select="row()"/>
   </xsl:apply-templates>
   <xsl:apply-templates select="node()">
    <xsl:sort select="id()"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

Yet it fails:

$ xmlstarlet tr sort.xsl example.xml 
Invalid number of arguments
xmlXPathCompiledEval: evaluation failed
Invalid number of arguments
xmlXPathCompiledEval: evaluation failed
Invalid number of arguments
xmlXPathCompiledEval: evaluation failed
<database>
  <table/>
</database>

1 Answer 1

3

I don't know anything about xmlstarlet, but I can say that your XSLT should really look like this...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml"  indent="yes" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="table">
        <xsl:copy>
            <xsl:apply-templates select="row">
                <xsl:sort select="id" data-type="number" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Note how you do not need () after element names in your XSLT.

See it in action at http://xsltransform.net/pNmBy1b

(I also note the tag xmlstarlet only has 20 followers. You might want to try out some other tools instead. See https://stackoverflow.com/tags/xslt/info for some help. xsltproc, perhaps?)

Sign up to request clarification or add additional context in comments.

3 Comments

xmlstarlet worked fine: xmlstarlet tr sort.xsl example.xml | xmlstarlet fo - ;) And thank you, this was the kickstart I needed.
Sidenote: xmlstarlet wants this to be version 1.0 for a larger file. Seems to be working nontheless.
My stylesheet was actually a version of "1.0" one, I just just forgot to change the version number. If a XSLT 1.0 process is given a stylesheet with version "2.0" then it runs in "forward compatibility" mode and will just ignore any xsl instructions it doesn't recognise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.