9.4. The <xsl:sort> Element

The <xsl:sort> element is used to reorder each node in the current node-set prior to processing the node list further with instruction elements. The current node-set for <xsl:sort> is the list of nodes returned from the select attribute of either <xsl:apply-templates> or <xsl:for-each>. Technically, <xsl:sort> is not classified as an instruction element because it is only allowed within two specific elements: <xsl:apply-templates> and <xsl:for-each>. The <xsl:sort> element is empty and has five optional attributes, as shown in the following element model definition. The main attribute is select, which contains an expression used as the key for sorting the node list.

<xsl:sort

  select = string-expression

  lang = { nmtoken }

  data-type = { "text" | "number" | qname-but-not-
					ncname }

  order = { "ascending" | "descending" }

  case-order = { "upper-first" | "lower-first" } />

If there is a value for the select attribute, each node in the current node-set is evaluated based on the expression in the select attribute and the result is converted to a string. This string is then used as the sort key. A sort key is the value used by the processor to perform the sort. The default value for the select attribute is ., which means that when the select attribute is not used, the sorting key is the content of the current node.

Example 9-8. Using <xsl:sort> to reorder elements.
					STYLESHEET:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="1.0">
<xsl:template match="text()" />
<xsl:template match="boulevard">
<xsl:copy>
      <xsl:for-each select="//block">
      <xsl:sort/>
            <xsl:copy>
            <xsl:attribute name="branch">
                  <xsl:value-of select="name(..)" />
            </xsl:attribute>
                  <xsl:value-of select="text()"/>
            </xsl:copy>
      </xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

RESULT:

<?xml version="1.0" encoding="utf-8"?>
<boulevard>
<block branch="thoroughfare">1st Street</block>
<block branch="thoroughfare">2nd Street</block>
<block branch="thoroughfare">3rd Street</block>
<block branch="boulevard">Carrol Circle</block>
<block branch="thoroughfare">First Street</block>
<block branch="boulevard">Highland Plaza</block>
<block branch="boulevard">Hutchens Avenue</block>
<block branch="boulevard">Old Chimney Road</block>
<block branch="boulevard">Panorama Street</block>
<block branch="thoroughfare">Second Street</block>
<block branch="thoroughfare">Third Street</block>
<block branch="boulevard">Wildwood Drive</block>
</boulevard>

In Example 9-8, we use our previous stylesheet for creating a new boulevard to reorder all the <block>s in Markup City, using the default values of the five <xsl:sort> attributes.

The sort works on the nodes that are selected by the <xsl:for-each> element, without changing the result of the instantiation of the rest of the instructions that follow it. The result is basically the same as in Example 9-7, except that now the <block>s are in alphabetical order based on the text they contain. Numbered streets come first, in ascending order, followed by the alphabetized streets.

When used within the <xsl:for-each> element, <xsl:sort> must come before any other instruction element, and the instructions are then applied to the nodes in sorted order. When used within <xsl:apply-templates>, <xsl:sort> can come before or after any <xsl:with-param> elements, which are the only other valid children of <xsl:apply-templates>.

Multiple <xsl:sort> elements are allowed in both <xsl:for-each> and <xsl:apply-templates>, and each <xsl:sort> element is used as an additional sort key; the first <xsl:sort> is used to specify the primary sort key, the second <xsl:sort> is used to specify the secondary sort key, and so on.

The four other attributes on <xsl:sort> are: lang, data-type, order, and case-order. Each of the attributes for <xsl:sort>, including select, are discussed in the following sections.

9.4.1. The select Attribute of <xsl:sort>

The select attribute contains an expression that returns a string. Each node in the current node-set is evaluated based on the expression in the select attribute, and the value is converted into a string, which is then used as the sort key for the sort.

Sort keys are often used to decide which child of the current node to sort on. If the node-set selected by the <xsl:apply-templates> or <xsl:for-each> elements returns a set of nodes that contains children, those children or descendants can be specified as sort keys. If there is no select attribute specified, the text of the current node is used as the default sort key. Note that using a select attribute to select children on a node-set that contains no children nodes may cause unpredictable results, if any. The select attribute can also be used to identify other objects to be used as a sort key, such as a parent or ancestor node, or an attribute value from the input element.

Using the same example from the previous section, we could modify our <xsl:sort> element to add a select attribute as follows:

<xsl:sort select="name(..)"/>

Using the name(..) value for the select attribute sorts the <block> elements in order based on the parent of the <block>, as shown in Example 9-9.

Example 9-9. Result of using the <xsl:sort> select attribute.
<?xml version="1.0" encoding="utf-8"?>
<boulevard>
<block branch="boulevard">Panorama Street</block>
<block branch="boulevard">Highland Plaza</block>
<block branch="boulevard">Hutchens Avenue</block>
<block branch="boulevard">Wildwood Drive</block>
<block branch="boulevard">Old Chimney Road</block>
<block branch="boulevard">Carrol Circle</block>
<block branch="thoroughfare">1st Street</block>
<block branch="thoroughfare">2nd Street</block>
<block branch="thoroughfare">3rd Street</block>
<block branch="thoroughfare">First Street</block>
<block branch="thoroughfare">Second Street</block>
<block branch="thoroughfare">Third Street</block>
</boulevard>

Notice in this example that although the <block>s are now sorted according to either boulevard or thoroughfare, they are still in document order within that sorting. In other words, each <block> is in the original order it was in from the input document, within the context of its parent element. We can do an additional sort using another <xsl:sort> element to specify the secondary sort key for the text of the <block>s, as shown in Example 9-10.

The additional <xsl:sort> in this example is using the default values to select the content of each <block> as the secondary sort key. The result is to sort the <block> elements by their text node within the original sort based on the parent of each <block>. In this case, the same effect can be accomplished using <xsl:sort/>, <xsl:sort select="."/>, or <xsl:sort select="text()"/>. The first two values of select apply to the content of the currently selected element, and in our example, each selected element is a <block> that only contains text, allowing the third value of select to also apply.

Example 9-10. Using multiple <xsl:sort> elements.
						MODIFIED TEMPLATE RULE:

<xsl:template match="boulevard">
<xsl:copy>
      <xsl:for-each select="//block">
      <xsl:sort select="name(..)"/>
      <xsl:sort/>
            <xsl:copy>
            <xsl:attribute name="branch">
                  <xsl:value-of select="name(..)" />
            </xsl:attribute>
                  <xsl:value-of select="text()"/>
            </xsl:copy>
      </xsl:for-each>
</xsl:copy>
</xsl:template>

RESULT:

<?xml version="1.0" encoding="utf-8"?>
<boulevard>
<block branch="boulevard">Carrol Circle</block>
<block branch="boulevard">Highland Plaza</block>
<block branch="boulevard">Hutchens Avenue</block>
<block branch="boulevard">Old Chimney Road</block>
<block branch="boulevard">Panorama Street</block>
<block branch="boulevard">Wildwood Drive</block>
<block branch="thoroughfare">1st Street</block>
<block branch="thoroughfare">2nd Street</block>
<block branch="thoroughfare">3rd Street</block>
<block branch="thoroughfare">First Street</block>
<block branch="thoroughfare">Second Street</block>
<block branch="thoroughfare">Third Street</block>
</boulevard>

9.4.2. The data-type Attribute of <xsl:sort>

The data-type attribute determines what kind of data the sort process uses: text, numeric, or some other unspecified type. The options for the value of data-type are: text, number, or a namespace-prefixed QName. A text value is the default that is assumed if no data-type is explicitly declared.

Text sorting converts the value of the node being sorted to a string prior to sorting, and uses sorting algorithms to sort lexicographically according to the language being used, defined in the lang attribute (see Section 9.4.5).

Numerical sorting converts the value of the node being sorted to a number prior to sorting, and then sorts the nodes numerically according to the value returned by that conversion. The lang attribute is ignored when the data-type is number.

A QName as the value of the data-type attribute may be used by some extension functions to define some other type of sorting criteria. In the case of a prefixed QName, the W3C specification for XSLT, section 10, states, “…the behavior in this case is not specified by this document.” The namespace for this value will determine the functionality of the sorting.

Example 9-11 shows the different results of sorting the numbers 1, 01, 001, 2, 02, and 002 using either text or number as the value of the data-type attribute.

Notice that the sorting order using number recognizes each value of 1 as a 1, ignoring the preceding zeros. As far as the processor is concerned, the values, 1, 01, and 001 all have the same value, so it does not sort them further than placing them together. Their original sequence in the input is maintained. The sorting order using text, however, recognizes the leading zeros as part of the string and sorts them accordingly. In text sorting, leading zeros are sorted before other numbers, so 002 comes before 01.

The values of text or number for the most part behave as you would expect. However, there is an element of “conversion” that occurs that can at first be surprising when there is mixed content of numbers and text in the sort key.

9.4.2.1. Sorting Numbers as Text

Using <xsl:sort> to sort numbers with a data-type of text converts the value of the number being sorted to a string prior to sorting. For example, sorting numbers using the text data-type, the number 13 will come before the number 6. This is because, as a text string, the value 1 as the first digit of 13 comes before 6. By contrast, if the same pair of values was sorted with a specified data-type of number, the 6 would precede the 13, as you would normally expect with numerical sequences.

Example 9-11. Results of sorting using number vs. using text.
							INPUT:

<?xml version="1.0"?>
<list>
      <num>002</num>
      <num>01</num>
      <num>1</num>
      <num>2</num>
      <num>001</num>
      <num>02</num>
</list>

STYLESHEET:

<xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="1.0">
<xsl:template match="/">
<xsl:for-each select="//num">
      <xsl:sort data-type="number"/>
      <xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

TEXT SORT RESULT:

      001
      002
      01
      02
      1
      2

NUMBER SORT RESULT:

     01
     1
     001
     002
     2
     02

9.4.2.2. Sorting Text as Numbers

When number is set for the data-type and the nodes being sorted contain a mixture of text and numbers, or just text, sorting is essentially ignored. This is because the use of number as the value for the data-type attribute causes the processor to attempt to convert the data to a number. If the data contains text, the result of the conversion will be the value "NaN" and the data will be sorted accordingly. This means that, if we tried to sort our <block> elements using number as the value of data-type, the result would be the same order as the inputs, because in all cases, the resulting value of the sort key for each <block> would be NaN.

9.4.3. The order Attribute of <xsl:sort>

The order attribute is used to specify whether the sort (alphabetic or numerical) is ascending or descending. These are the only two values allowed for the order attribute. The default order is ascending, so the order attribute only needs to be specified if descending order is needed. This would then reverse the order of the results. For example, using Markup City as input, the <block>s in our new <boulevard> would end up in reverse order, as shown in Example 9-12.

9.4.4. The case-order Attribute of <xsl:sort>

The case-order attribute provides the means to specify a case-specific sort, sorting either uppercase at the top, using the upper-first value, or lowercase at the top, using the lower-first value.

In Example 9-13, we have created duplicate <block>s, except for changing the case of every duplicated block name (panorama and Panorama, highland and Highland, etc.).

The default value of case-order is language-dependent, so that for English, for example, it would be upper-first. Language is specified using the lang attribute, discussed in the next section.

Example 9-12. Using the order attribute with the descending value.
						STYLESHEET:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="text()" />

<xsl:template match="boulevard">
      <xsl:copy>
            <xsl:for-each select="//block">
                  <xsl:sort order="descending"/>
                  <xsl:copy>
                        <xsl:value-of select="text()"/>
                  </xsl:copy>
            </xsl:for-each>
      </xsl:copy>
</xsl:template>
</xsl:stylesheet>

RESULT:

<?xml version="1.0" encoding="utf-8"?>
<boulevard>
<block>Wildwood Drive</block>
<block>Third Street</block>
<block>Second Street</block>
<block>Panorama Street</block>
<block>Old Chimney Road</block>
<block>Hutchens Avenue</block>
<block>Highland Plaza</block>
<block>First Street</block>
<block>Carrol Circle</block>
<block>3rd Street</block>
<block>2nd Street</block>
<block>1st Street</block>
</boulevard>

Example 9-13. Using <xsl:sort> with the case-order attribute.
						INPUT:

<boulevard>
      <block>panorama Street</block>
      <block>Panorama Street</block>
      <block>highland Plaza</block>
      <block>Highland Plaza</block>
      <block>hutchens Avenue</block>
      <block>Hutchens Avenue</block>
      <block>wildwood Drive</block>
      <block>Wildwood Drive</block>
      <block>old Chimney Road</block>
      <block>Old Chimney Road</block>
      <block>carrol Circle</block>
      <block>Carrol Circle</block>
</boulevard>

STYLESHEET:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="text()" />
<xsl:template match="boulevard">
      <xsl:copy>
            <xsl:for-each select="//block">
                  <xsl:sort case-order="upper-first"/>
                  <xsl:copy>
                        <xsl:value-of select="text()"/>
                  </xsl:copy>
            </xsl:for-each>
      </xsl:copy>
</xsl:template>
</xsl:stylesheet>

RESULT:

<?xml version="1.0" encoding="utf-8"?>
<boulevard>
<block>Carrol Circle</block>
<block>carrol Circle</block>
<block>Highland Plaza</block>
<block>highland Plaza</block>
<block>Hutchens Avenue</block>
<block>hutchens Avenue</block>
<block>Old Chimney Road</block>
<block>old Chimney Road</block>
<block>Panorama Street</block>
<block>panorama Street</block>
<block>Wildwood Drive</block>
<block>wildwood Drive</block>
</boulevard>

When case-order is declared as lower-first, the reverse would apply, as follows:

<?xml version="1.0" encoding="utf-8"?>
<boulevard>
<block>carrol Circle</block>
<block>Carrol Circle</block>
<block>highland Plaza</block>
<block>Highland Plaza</block>
<block>hutchens Avenue</block>
<block>Hutchens Avenue</block>
<block>old Chimney Road</block>
<block>Old Chimney Road</block>
<block>panorama Street</block>
<block>Panorama Street</block>
<block>wildwood Drive</block>
<block>Wildwood Drive</block>
</boulevard>

9.4.5. The lang Attribute of <xsl:sort>

The lang attribute is used to specify the language that is being used for sorting. It accepts a two-character language value, such as “en” for English, from the ISO 639 specification, or a subcode, such as “en-US,” for specific countries defined by the ISO 3166 specification. The XML recommendation[1] suggests using lowercase prefixes, followed by uppercase suffixes, even though the value of the lang attribute is not case-sensitive.

[1] The XML recommendation can be found at http://www.w3.org/TR/REC-xml.

Using the lang attribute enables specific nuances of alphabetic order with languages other than English to be explicitly accommodated in <xsl:sort>. If there is no language specified, the default value for the lang attribute is based on the language that is specified by the system environment or regional settings.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset