Vince42
Vince42

Reputation: 234

XSL apply-templates with sorting does not sort

I have an XML document

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <timeperiods>
        <timeperiod>
            <day>1</day>
            <period>1</period>
        </timeperiod>
        <timeperiod>
            <day>1</day>
            <period>2</period>
        </timeperiod>
        <timeperiod>
            <day>2</day>
            <period>1</period>
        </timeperiod>
        <timeperiod>
            <day>2</day>
            <period>2</period>
        </timeperiod>
    </timeperiods>
</document>

I am applying an XSL as follows

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    
    <xsl:template match="/">
        <xsl:apply-templates select="/document/timeperiods">
            <xsl:sort select="number(period)" data-type="number" order="descending"/>
            <xsl:sort select="number(day)" data-type="number" order="descending"/>
        </xsl:apply-templates>
    </xsl:template>
    
    <xsl:template match="timeperiod">
        <xsl:value-of select="concat(period, '-', day, ' ')"/>
    </xsl:template>
</xsl:stylesheet>

I would expect that my template timeperiod would result in 1-1 1-2 2-1 2-2, but instead it results in 1-1 2-1 1-2 2-2, which is the original order in the XML file.

I played around with the number() function and the attributes data-type and order, but no matter which variant I applied, I am always getting the same result.

What am I missing here?

Upvotes: 0

Views: 188

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116993

To get a result like the one you expect, change:

    <xsl:apply-templates select="/document/timeperiods">
        <xsl:sort select="number(period)" data-type="number" order="descending"/>
        <xsl:sort select="number(day)" data-type="number" order="descending"/>
    </xsl:apply-templates>

to:

    <xsl:apply-templates select="/document/timeperiods/timeperiod">
        <xsl:sort select="period" data-type="number" order="ascending"/>
        <xsl:sort select="day" data-type="number" order="ascending"/>
    </xsl:apply-templates>

Or do simply:

XSLT 2.0

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
    
<xsl:template match="/document">
    <xsl:for-each select="timeperiods/timeperiod">
        <xsl:sort select="period" data-type="number" order="ascending"/>
        <xsl:sort select="day" data-type="number" order="ascending"/>
        <xsl:value-of select="period, day" separator="-"/>
        <xsl:text> </xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163352

There is only one element named timeperiods, and sorting a sequence of length one is not going to be useful.

You want to sort the timeperiod elements, so those are the ones you should select. You haven't given the context of the xsl:apply-templates instruction; obviously that will affect how you select them.

Upvotes: 4

Related Questions