I would like to sort elements based on derived value based on child elements. The resulting value cannot be calculated using XPath ( sum, concatetc.), but it can be done with XSL ( xsl:choose, xsl:ifetc.).
I would use the EXSLT function extension, but it is not available. XSLT 1.0 environment, Xalan-C ++ version 1.10 with the common and installed EXSLT extensions.
EDIT . Changed the example to emphasize that the derived value that I need to group cannot be calculated using simple node / xpath functions in statements xsl:sort.
My goal is to list current medications to inactive, sorted by start date. The logic for determining whether a medicine is current depends on whether it has been canceled, and other business logic has not expired.
Given this XML:
<?xml version="1.0"?>
<medications>
<medication>
<name>med1</name>
<status>canceled</status>
<startTime>2012-02-01T00:00:00Z</startTime>
<endTime>2012-12-31T00:00:00Z</endTime>
</medication>
<medication>
<name>med2</name>
<status />
<startTime>2012-01-01T00:00:00Z</startTime>
<endTime>2012-01-07T00:00:00Z</endTime>
</medication>
<medication>
<name>med3</name>
<status />
<startTime>2012-01-01T00:00:00Z</startTime>
</medication>
</medications>
A sorted list of drugs will be created in the stylesheet, including information omitted from the example data (customer, pharmacy location, etc.) and node parent data (patient address, primary care doctor, etc.). In this example, I simply create a simple sorted list that shows that the medication node can be traversed:
<?xml version="1.0" encoding="utf-8"?>
<medications>
<medication isCurrent="1" name="med3" />
<medication isCurrent="0" name="med1" />
<medication isCurrent="0" name="med2" />
</medications>
The best I can come up with is to pre-compute the derived value in the EXSLT node-set (along with other values needed for sorting), and use the key to search for the medicine item using generate-id:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output encoding="utf-8" method="xml" indent="yes" />
<xsl:key name="medications-by-id" match="medication" use="generate-id()" />
<xsl:variable name="medication-sorter">
<xsl:for-each select="//medication">
<item id="{generate-id(.)}">
<xsl:attribute name="isCurrent">
<xsl:apply-templates mode="isCurrentMedication" select="." />
</xsl:attribute>
<xsl:attribute name="startTime">
<xsl:value-of select="startTime/text()" />
</xsl:attribute>
</item>
</xsl:for-each>
</xsl:variable>
<xsl:template match="medications">
<hardcoded><xsl:value-of select="key('medications-by-id',generate-id(medication[2]))/name/text()"/></hardcoded>
<medications>
<xsl:for-each select="exsl:node-set($medication-sorter)/item">
<xsl:sort select="@isCurrent" order="descending" />
<xsl:sort select="@startTime" order="descending" />
<medication>
<xsl:attribute name="isCurrent">
<xsl:value-of select="@isCurrent" />
</xsl:attribute>
<xsl:attribute name="name">
<xsl:value-of select="key('medications-by-id',@id)/name/text()" />
</xsl:attribute>
</medication>
</xsl:for-each>
</medications>
</xsl:template>
<xsl:template mode="isCurrentMedication" match="medication">
<xsl:choose>
<xsl:when test="(status/text()='canceled') or (status/text()='discontinued') or (status/text()='inactive')">0</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="name/text()='med2'">0</xsl:when>
<xsl:when test="name/text()='med3'">1</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
, . generate-id ( [2]) node , @id node, :
<?xml version="1.0" encoding="utf-8"?>
<medications>
<hardcoded>med2</hardcoded>
<medication isCurrent="1" name="" />
<medication isCurrent="0" name="" />
<medication isCurrent="0" name="" />
</medications>
Xalan Java 2.7.1 .
, copy-of $medication-sorter node -set, , , .
/ , xsl:template?