0

I'm trying to use xsl:sort to invert a list in XML/XSLT, but it doesn't appear to be doing anything.

example.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text" encoding="UTF-8"/>
  <xsl:template match="/">
   <xsl:for-each select="CONTAINER/PARAM">
    <xsl:sort data-type="number" order="descending" select="position()"/>
    Battleaxe = <xsl:value-of select="name()" /><xsl:number value="position()" format="1" />
   </xsl:for-each>
  </xsl:template>
 </xsl:stylesheet>

example.xml

<CONTAINER>
 <PARAM>foo</PARAM>
 <PARAM>bar</PARAM>
 <PARAM>baz</PARAM>
 <PARAM>quix</PARAM>
</CONTAINER>

Expected result:

Battleaxe = PARAM4
Battleaxe = PARAM3
Battleaxe = PARAM2
Battleaxe = PARAM1

Actual result:

Battleaxe = PARAM1
Battleaxe = PARAM2
Battleaxe = PARAM3
Battleaxe = PARAM4

If it helps, I'm using xsltproc, version:

Using libxml 20903, libxslt 10117 and libexslt 813
xsltproc was compiled against libxml 20626, libxslt 10117 and libexslt 813
libxslt 10117 was compiled against libxml 20626
libexslt 813 was compiled against libxml 20626

When running with xsltproc --verbose, I see no mention of sorting anywhere in the output

I've got to be missing something obvious here...

EDIT:

OK, looks like position() is always in ascending order, even if you previously reversed it. I guess that makes sense

This differs to How to do an XSL:for-each in reverse order in that I have no such id attribute and just want to use some positional based logic. Is there something like a length() function I can use to reverse it manually?

LordAro
  • 1,269
  • 3
  • 18
  • 35
  • Possible duplicate of [How to do an XSL:for-each in reverse order](https://stackoverflow.com/questions/5882519/how-to-do-an-xslfor-each-in-reverse-order) – derloopkat Aug 16 '18 at 17:36
  • not quite, although it does give me a few clues. That answer uses @id in the output, whereas I have no such id and want to use position() (or some other similar function) – LordAro Aug 16 '18 at 17:54
  • If you use `Battleaxe = `, as done in https://xsltfiddle.liberty-development.net/94hvTzu, you will note that the elements are processed in reverse order, I am not sure why you expect the `position()` function to give reversed values. – Martin Honnen Aug 16 '18 at 17:58
  • yeah, I don't actually care about the elements in this context, I just need a reverse ordered list of numbers :) – LordAro Aug 16 '18 at 18:01

2 Answers2

1

If you want to have the sequence of numbers 4, 3, 2, 1 then instead of position() output/compute last() - position() + 1. But you don't have to sort for doing that.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
1

In the xsl:sort/@select expression, the context sequence is the unsorted sequence, so position() gives you the position of an item in its original unsorted order. Hence sorting with select="position()" order="descending" data-type="number" reverses the input order.

Within the body of the xsl:for-each, by contrast, position() gives you the position of the item in the sorted sequence.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164