2

I'm a newbie in XSLT. I just come up with a question and hope someone can help.

Assume I have a source xml,

<?xml version="1.0"?>
<docroot>
    <vc6>foo</vc6>
    <vc7>bar7</vc7>
    <vc8 arch="x64">amd64demo</vc8>
    <vc7>foo7</vc7>
    <vc6>bar</vc6>
</docroot> 

I'd like to turn it into:

<?xml version="1.0"?>
<docroot>
    <vc6>bar</vc6>
    <vc6>foo</vc6>
    <vc7>bar7</vc7>
    <vc7>foo7</vc7>
    <vc8 arch="x64">amd64demo</vc8>
</docroot> 

that is,

  1. child elements of should be sorted by element name, so <vc6> comes before <vc7> .
  2. If two children have the same element name, they should be sorted by their text value, so 'bar' is ahead of 'foo'.

How to write the xsl? Thank you.

Jimm Chen
  • 3,411
  • 3
  • 35
  • 59

2 Answers2

5

Revision of legoscia's answer:

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="node()">
      <xsl:sort select="name()" />
      <xsl:sort select="." />
    </xsl:apply-templates>
  </xsl:copy>
</xsl:template>
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Oops, your two elements are not in pairs. Even if I duplicate the end tag, side by side, to make it well-formed. xsltproc 2.7.8 outputs only ```` , not the expected result. – Jimm Chen Jun 14 '12 at 14:59
  • Note that if you want to run that thorough e.g xsltproc you'll need to wrap it in ... – Eric Jun 02 '23 at 15:24
4

There are some examples of how to use xsl:sort in this answer. Something like this should work for you:

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="node()">
      <xsl:sort select="name()" />
      <xsl:sort select="." />
    </xsl:apply-templates>
  </xsl:copy>
</xsl:template>
Community
  • 1
  • 1
legoscia
  • 39,593
  • 22
  • 116
  • 167
  • 1
    Thank you, you give the right answer. But, Could you update your answer so that attributes can be copied? Currently, with attribute in source xml, xsltproc 2.7.8 says "Attribute nodes must be added before any child nodes to an element". – Jimm Chen Jun 12 '12 at 14:06
  • No, it's still wrong. If there's a child element called AA and an attribute called ZZ, it will try to put the child element before the attribute. – Michael Kay Jun 12 '12 at 16:37
  • 1
    Sorry, legoscia, I notice that you just swapped the ``@*`` and ``node()``, but still the error: "Attribute nodes must be added before any child nodes to an element". Have you verify it with some xslt software? – Jimm Chen Jun 14 '12 at 14:54
  • Right, now it's actually tested :) Works for me with xsltproc. – legoscia Jun 14 '12 at 15:25
  • as reported, I also get "Attribute nodes must be added before any child nodes to an element" with xsltproc, however @MichaelKay answer works just fine for me – Peter Butkovic Jan 26 '17 at 17:43