SharpBarb
SharpBarb

Reputation: 1590

XSLT Not Sorting Across Multiple Template Matches

XSLT 1.0

I am sorting text message output and am trying to have the sms and mms messages listed by date. The data is stuctured something like:

  <smses>
    <sms address="555555001" date="1517372455000" readable_date="Jan 30, 2018" message="Hello" name="John" />
    <sms address="555555001" date="1517372455004" readable_date="Jan 30, 2018" message="Hello" name="John" />
    <sms address="555555009" date="1517372458000" readable_date="Jan 30, 2018" message="Hello" name="Jane" />
    <sms address="555555009" date="1517372458001" readable_date="Jan 30, 2018" message="Hello" name="Jane" />
    <sms address="555555009" date="1517372458002" readable_date="Jan 30, 2018" message="Hello" name="Jane" />
    <sms address="555555001" date="1517372455005" readable_date="Jan 30, 2018" message="Hello" name="John" />
    <mms address="555555001" date="1517372455001" readable_date="Jan 30, 2018" message="Hello" name="John" />
    <mms address="555555001" date="1517372455002" readable_date="Jan 30, 2018" message="Hello" name="John" />
    <mms address="555555001" date="1517372455003" readable_date="Jan 30, 2018" message="Hello" name="John" />
   </smses>

I am generating a key that filters by "John" (@readable_date exists in actual data)

<xsl:key name="shortDate" match="sms[@name='John']|mms[@name='John']" use="@readable_date" />

<xsl:template match="/">    
    <xsl:apply-templates select="/smses/sms[generate-id(.)=generate-id(key('shortDate',@readable_date)[1])] | /smses/mms[generate-id(.)=generate-id(key('shortDate',@readable_date)[1])]">
      <xsl:sort select="@date" data-type="number" />
    </xsl:apply-templates>  
</xsl:template>

<xsl:template match="sms|mms">
        <xsl:for-each select="key('shortDate',@readable_date)">
            <xsl:value-of select="@name" /><xsl:value-of select="@readable_date" /><xsl:value-of select="@message" />
        </xsl:for-each>
</xsl:template> 

No matter what I try, the mms messages always come after the sms messages instead of all the nodes being sorted by date.

Upvotes: 1

Views: 70

Answers (2)

Michael Kay
Michael Kay

Reputation: 163352

You are grouping elements by @readable_date, and sorting the groups; you are then outputting the contents of each group without further sorting (i.e., each group is output in document order).

But in your sample data, all the elements have the same @readable_date, so there is only one group, so sorting the groups achieves nothing. You need to sort the items within the group, that is the xsl:for-each instruction in the match="sms|mms" template rule needs an xsl:sort specification.

Upvotes: 1

Aniket V
Aniket V

Reputation: 3247

I don't think you would need a <xsl:key> to be defined if you are only looking for messages sent to John. You can apply a <xsl:for-each> filtered on John and then apply the sorting on @date. Below is the XSLT template.

<xsl:template match="smses">
    <xsl:for-each select="*[@name='John']">
        <xsl:sort select="@date" order="ascending" data-type="number" />
        <output>
            <xsl:value-of select="@message" />
        </output>
    </xsl:for-each>
</xsl:template>

In order to validate the output, I have made some changes to the @message value to know the difference. Below is the updated input XML.

<smses>
    <sms address="555555001" date="1517372455000" readable_date="Jan 30, 2018" message="Hello, This is SMS 1 for John." name="John" />
    <sms address="555555001" date="1517372455004" readable_date="Jan 30, 2018" message="Hello, This is SMS 2 for John." name="John" />
    <sms address="555555009" date="1517372458000" readable_date="Jan 30, 2018" message="Hello, This is SMS 1 for Jane." name="Jane" />
    <sms address="555555009" date="1517372458001" readable_date="Jan 30, 2018" message="Hello, This is SMS 2 for Jane." name="Jane" />
    <sms address="555555009" date="1517372458002" readable_date="Jan 30, 2018" message="Hello, This is SMS 3 for Jane." name="Jane" />
    <sms address="555555001" date="1517372455005" readable_date="Jan 30, 2018" message="Hello, This is SMS 3 for John." name="John" />
    <mms address="555555001" date="1517372455001" readable_date="Jan 30, 2018" message="Hello, This is MMS 1 for John." name="John" />
    <mms address="555555001" date="1517372455002" readable_date="Jan 30, 2018" message="Hello, This is MMS 2 for John." name="John" />
    <mms address="555555001" date="1517372455003" readable_date="Jan 30, 2018" message="Hello, This is MMS 3 for John." name="John" />
</smses>

Using the above XML, when the XSLT template is applied, the below sorted output is generated

<output>Hello, This is SMS 1 for John.</output>
<output>Hello, This is MMS 1 for John.</output>
<output>Hello, This is MMS 2 for John.</output>
<output>Hello, This is MMS 3 for John.</output>
<output>Hello, This is SMS 2 for John.</output>
<output>Hello, This is SMS 3 for John.</output>

Upvotes: 1

Related Questions