I've been looking at threads about sorting XML using <xsl:sort> and variables, and still can't get my sorting to work. Here's some XML structure for context:
<records>
<record>
<contributors>
<authors>
<author>Author 1</author>
<author>Author 2</author>
</authors>
</contributors>
<titles>
<title>I'm a Title!</title>
<secondary-title></secondary-title>
</titles>
<dates>
<year>1901</year>
</dates>
</record>
<record>...</record>
<record>...</record>
</records>
And here's the relevant XSL:
<xsl:variable name="sortby"
select="contributors/authors/author[1]" as="element()*"/>
<xsl:for-each select="//record">
<xsl:sort select="$sortby" order="ascending"/>
[a bunch of HTML to render the records as a bibliography]
</xsl:for-each>
If I copy the string in the variable's "select" attributes and paste it into sort, like this:
<xsl:sort select="contributors/authors/author[1]" order="ascending">
then it works. With the variable, it doesn't. I tried it both with and without as="element()*" -- Help?
It is not possible in general to perform dynamic evaluation of XPath expressions -- neither in XSLT/Xpath 1.0 or in XSLT/Xpath 2.0.
This said, one can always implement sorting guided by variables, if there are some limitations on their contents.
Here is an example that solves your specific problem and a class of similar problems:
<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:param name="pSortName" select="'authors'"/>
<xsl:param name="pSortPosition" select="1"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="records">
<records>
<xsl:apply-templates>
<xsl:sort select=
".//*[name()=$pSortName]/*
[position()=$pSortPosition]"/>
</xsl:apply-templates>
</records>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on this XML document:
<records>
<record>
<contributors>
<authors>
<author>X.Y.Z</author>
<author>A.B.C</author>
</authors>
</contributors>
<titles>
<title>Title B</title>
<secondary-title>Title AB</secondary-title>
</titles>
<dates>
<year>1901</year>
</dates>
</record>
<record>
<contributors>
<authors>
<author>T.U.V</author>
<author>D.E.F</author>
</authors>
</contributors>
<titles>
<title>Title A</title>
<secondary-title>Title BA</secondary-title>
</titles>
<dates>
<year>2001</year>
</dates>
</record>
</records>
the wanted, correct result (the records sorted by first author) is produced:
<records>
<record>
<contributors>
<authors>
<author>T.U.V</author>
<author>D.E.F</author>
</authors>
</contributors>
<titles>
<title>Title A</title>
<secondary-title>Title BA</secondary-title>
</titles>
<dates>
<year>2001</year>
</dates>
</record>
<record>
<contributors>
<authors>
<author>X.Y.Z</author>
<author>A.B.C</author>
</authors>
</contributors>
<titles>
<title>Title B</title>
<secondary-title>Title AB</secondary-title>
</titles>
<dates>
<year>1901</year>
</dates>
</record>
</records>
If we change the parameters to:
<xsl:param name="pSortName" select="'authors'"/>
<xsl:param name="pSortPosition" select="2"/>
then the transformation sorts using as sort-key the second author.
If we change the parameters to:
<xsl:param name="pSortName" select="'titles'"/>
<xsl:param name="pSortPosition" select="1"/>
then the transformation sorts using as sort-key the titles/title element.
If we change the parameters to:
<xsl:param name="pSortName" select="'titles'"/>
<xsl:param name="pSortPosition" select="2"/>
then the transformation sorts using as sort-key the titles/secondary-title element.
Do note: Here we assume that there will be a unique descendent of any element being sorted, whose name is equal to the value specified in pSortName. We also assume that this element has children elements and pSortPosition specifies the position of the child to be used as a sort key.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With