XSLT sort using derived / computed values ​​without EXSLT functions

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>
    <!-- other elements omitted -->
  </medication>
  <medication>
    <name>med2</name>
    <status />
    <startTime>2012-01-01T00:00:00Z</startTime>
    <endTime>2012-01-07T00:00:00Z</endTime>
    <!-- other elements omitted -->
  </medication>
  <medication>
    <name>med3</name>
    <status />
    <startTime>2012-01-01T00:00:00Z</startTime>
    <!-- other element omitted -->
  </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 key lookup works -->
    <hardcoded><xsl:value-of select="key('medications-by-id',generate-id(medication[2]))/name/text()"/></hardcoded>

    <!-- but key lookup from the sort helper does not -->
    <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>
        <!-- omitted date checking logic not relevent to this question, so just hardcoded -->
        <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?

+3
2

:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="xml" indent="yes"/>
<xsl:template match="books">
    <xsl:copy>
        <xsl:for-each select="book">
            <xsl:sort select="status" order="descending"/>
            <xsl:sort select="number(count) &gt; 0" order="descending"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>
+2

. , , .
, , , , , .

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/">
        <output>
            <xsl:for-each select="books/book">
                <xsl:sort select="status" order="descending"/>
                <xsl:sort select="count" order="descending"/>
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </output>
    </xsl:template>

</xsl:stylesheet>

, ; , .

<?xml version="1.0" encoding="UTF-8"?>
<output>
    <book>
        <title>Three</title>
        <price>30</price>
        <count>3</count>
        <status>Published</status>
    </book>
    <book>
        <title>One</title>
        <price>10</price>
        <count>1</count>
        <status>Published</status>
    </book>
    <book>
        <title>Zero</title>
        <price>5</price>
        <count>0</count>
        <status>Published</status>
    </book>
    <book>
        <title>Two</title>
        <price/>
        <count>0</count>
        <status>Out of Print</status>
    </book>
</output>
0

All Articles