Lily B
Lily B

Reputation: 1

How to sorting parent elements only by their root nodes in XSLT?

I got a XML file looks like

<root>
<number>1</number>
<number>
    <deleted>0</deleted>
    3
</number>
<number>2</number>
</root>

I want to sort the file only by valid value, like the valid value of the second element is 3. I only need the value not contained in any child elements. So the desired output looks like:

<root>
<number>1</number>
<number>2</number>
<number>
 <deleted>0</deleted>
 3
</number>
</root>

I tried to use except in sorting but it didn't work. My stylesheet:

    <xsl:template match="root">
        <xsl:copy>
            <xsl:for-each select="number">
                <xsl:sort select=". except del"/>
                <xsl:copy-of select="." />
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

Thank you!

Add: I changed the XML into

<root>
    <number>1</number>
    <number>
        1
        <deleted>0</deleted>
        3
    </number>
    <number>2</number>
</root>

Insert a node before the <deleted> tag, then the <xsl:sort select="text()"> can't work. An error message says XTTE1020: A sequence of more than one item is not allowed as the @select attribute of xsl:sort (text("1"), text("3")) Please help :(

Upvotes: 0

Views: 140

Answers (1)

Tim C
Tim C

Reputation: 70638

. matches the current node, which is number, so doing . except deleted is not really going to achieve much. You need to sort on the child node that is not deleted

 <xsl:sort select="node() except deleted" data-type="number" />

Although, it is really only the text node you want, so you don't really need except at all

<xsl:sort select="text()" data-type="number" />

However, this will only work if you strip out white-space nodes first with xsl:strip-space (otherwise you will get an error about the sort selecting a sequence of nodes). Alternatively you could do <xsl:sort select="text()[normalize-space()]" data-type="number" />

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:strip-space elements="*" />

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="root">
    <xsl:copy>
      <xsl:for-each select="number">
        <xsl:sort select="text()" data-type="number" />
        <xsl:copy-of select="." />
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Note, the above stylesheet will work in XSLT 1.0, but if you are using XSLT 2.0, then you can simplify this a bit with xsl:perform-sort instead...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:strip-space elements="*" />

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="root">
    <xsl:copy>
      <xsl:perform-sort select="number">
        <xsl:sort select="text()" data-type="number" />
      </xsl:perform-sort>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

EDIT: And, if you have multiple child text nodes, then do this to concatenate them...

<xsl:sort select="string-join(text()/normalize-space())" data-type="number" />

Upvotes: 1

Related Questions