Reputation: 1312
Given input XML of the following form:
<root>
<alpha />
<beta />
<gamma />
</root>
I'd like to be able to process the elements in a custom order, defined in an XSL stylesheet. I've tried:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://tempurl.org"
exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<!-- Elements should be processed in the following order: -->
<my:sorting>
<my:item id="beta" />
<my:item id="gamma" />
<my:item id="alpha" />
</my:sorting>
<xsl:template match="root">
<xsl:variable name="sortOrder" select="document('')/*/my:sorting/my:item" />
<xsl:apply-templates>
<xsl:sort select="count($sortOrder[@id=name()]/preceding-sibling::*)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*">
<xsl:value-of select="name()" />
</xsl:template>
</xsl:stylesheet>
but this has no effect on the ordering.
Is a solution along these lines possible? Have I just made some simple error in the XSL?
(I am aware of a different solution involving string-length
and substring-before
, but I'd like to know how to get things working in a similar fashion to that above, if possible.)
Upvotes: 2
Views: 1156
Reputation: 70598
The problem is with the xsl:sort
<xsl:sort select="count($sortOrder[@id=name()]/preceding-sibling::*)"/>
In the expression, name()
is context sensitive. It is part of a condition applying to a node set (sortOrder
) and so will return the name of the sortOrder
element. Essentially, it is looking for sortOrder
elements whose name equal their id
attribute.
To get around this, use the current()
function
<xsl:sort select="count($sortOrder[@id=name(current())]/preceding-sibling::*)"/>
An alternate approach, although not as elegant, is to define the sort order as a delimited string, like so:
<xsl:variable name="sortOrder" select="'|beta|alpha|gamma|'" />
Then your sort expression becomes this, to essential sort by the position of the element name in the string.
<xsl:sort select="string-length(substring-before($sortOrder, concat('|', name(), '|')))" data-type="number"/>
In this case, name()
is not part of an condition, so the context is the elements you are sorting.
Upvotes: 3