In XSLT, how can you sort using an indirect key?
I am having trouble getting xsl:sort
to understand the scope of the attributes I am referencing. Here is an XML sample document to illustrate:
<Root>
<DrinkSelections>
<Drink id=1000 name="Coffee"/>
<Drink id=1001 name="Water"/>
<Drink id=1002 name="Tea"/>
<Drink id=1003 name="Almost But Not Quite Entirely Unlike Tea"/>
</DrinkSelections>
<CustomerOrder>
<Drinks>
<Drink oid="1001"/>
<Drink oid="1002"/>
<Drink oid="1003"/>
</Drinks>
</CustomerOrder
</Root>
I want to produce a list of drinks (sorted by name) contained in the CustomerOrder. Here is the XSLT code I am fiddling with:
<xsl:for-each select="/Root/CustomerOrder/Drinks/Drink">
<xsl:sort select="/Root/DrinkSelec开发者_JAVA技巧tions/Drink[@id = @oid]/@name"/>
<xsl:variable name=var_oid select="@oid"/>
<xsl:value-of select="/Root/DrinkSelections/Drink[@id = $var_oid]/@name"/>
</xsl:for-each>
Apparently, the xsl:sort
command is trying to apply the "oid" attribute to the Drink elements in DrinkSelections, rather than local Drink element.
I can get around this using a variable, as in the xsl:value-of
statement. But since xsl:sort
must be the first statement after the xsl:for-each
statement, I can't insert the xsl:variable
statement before xsl:sort
.
Is there a way to explicitly state that the attribute value should be taken from the "local" element?
You are missing the current()
function.
<xsl:for-each select="/Root/CustomerOrder/Drinks/Drink">
<xsl:sort select="/Root/DrinkSelections/Drink[@id = current()/@oid]/@name"/>
<xsl:value-of select="/Root/DrinkSelections/Drink[@id = current()/@oid]/@name"/>
</xsl:for-each>
But more prominently, you are missing XSL keys, for readability and performance:
<xsl:key name="kDrinkById" match="DrinkSelections/Drink" use="@id" />
<!-- ... later ... -->
<xsl:for-each select="/Root/CustomerOrder/Drinks/Drink">
<xsl:sort select="key('kDrinkById', @oid)/@name"/>
<xsl:value-of select="key('kDrinkById', @oid)/@name"/>
</xsl:for-each>
An you are probably not using templates right, because if you did, your xsl:for-each
select expression would not start at the root.
<xsl:template match="Root">
<xsl:apply-templates select="CustomerOrder/Drinks" />
</xsl:template>
<xsl:template match="CustomerOrder/Drinks">
<xsl:apply-templates select="Drink">
<xsl:sort select="key('kDrinkById', @oid)/@name"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="CustomerOrder/Drinks/Drink">
<xsl:value-of select="key('kDrinkById', @oid)/@name"/>
</xsl:template>
Note that I removed the for-each also. Every xsl:for-each
avoided is a step towards better XSLT code (very rare exceptions apply).
精彩评论