hopkinch
hopkinch

Reputation: 25

XSLT Sort by Date Not Working

I'm trying to get the most recent tax data for a locality. I'm using the following input and xslt transformation:

<?xml version='1.0' encoding='UTF-8'?>
<root>
    <row>
        <Applies_To>Abbott Twp (Pennsylvania:Potter)</Applies_To>
        <Tax_Code>42530035</Tax_Code>
        <Tax>Local City Withholding (Resident)</Tax>
        <Start_Date>2011-07-01</Start_Date>
        <Tax_Rate>0.005</Tax_Rate>
    </row>
    <row>
        <Applies_To>Abbott Twp (Pennsylvania:Potter)</Applies_To>
        <Tax_Code>42530035</Tax_Code>
        <Tax>Local City Withholding (Resident)</Tax>
        <Start_Date>2015-01-01</Start_Date>
        <Tax_Rate>0.099</Tax_Rate>
    </row>
    <row>
        <Applies_To>Abbott Twp (Pennsylvania:Potter)</Applies_To>
        <Tax_Code>42530035</Tax_Code>
        <Tax>Local City Withholding (Work)</Tax>
        <Start_Date>2011-07-01</Start_Date>
        <Tax_Rate>0</Tax_Rate>
    </row>
    <row>
        <Applies_To>Abbottstown Boro (Pennsylvania:Adams)</Applies_To>
        <Tax_Code>42010033</Tax_Code>
        <Tax>Local City Withholding (Resident)</Tax>
        <Start_Date>2011-07-01</Start_Date>
        <Tax_Rate>0.005</Tax_Rate>
    </row>
    <row>
        <Applies_To>Abbottstown Boro (Pennsylvania:Adams)</Applies_To>
        <Tax_Code>42010033</Tax_Code>
        <Tax>Local City Withholding (Work)</Tax>
        <Start_Date>2012-07-01</Start_Date>
        <Tax_Rate>0.01</Tax_Rate>
    </row>
    <row>
        <Applies_To>Abbottstown Boro (Pennsylvania:Adams)</Applies_To>
        <Tax_Code>42010033</Tax_Code>
        <Tax>xxxx</Tax>
        <Start_Date>2012-07-01</Start_Date>
        <Tax_Rate>0.01</Tax_Rate>
        
    </row>

</root>

XSLT:

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

    <xsl:template match="/">
        <root>
            <xsl:for-each-group select="*/row" 
                group-by="Tax_Code">
                <xsl:sort order="descending" select="Start_Date"/>
                <row>
                    <Tax_Code>
                        <xsl:attribute name="Code" select="current-grouping-key()"/>
                            <xsl:for-each-group select="current-group()" group-by="Tax">
                                <xsl:call-template name="Tax_Code"/>
                            </xsl:for-each-group>  
                    </Tax_Code>
                </row>
            </xsl:for-each-group>
        </root>
    </xsl:template>
    
    <xsl:template name="Tax_Code">
        <Tax_Rate>
            <xsl:attribute name="taxtype">
                <xsl:value-of select="Tax"/>
            </xsl:attribute>
            <xsl:value-of select="Tax_Rate"/>
        </Tax_Rate>
        <date>
            <xsl:value-of select="Start_Date"></xsl:value-of>
        </date>

    </xsl:template>

</xsl:stylesheet>

I then get the following output:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <row>
      <Tax_Code Code="42530035">
         <Tax_Rate taxtype="Local City Withholding (Resident)">0.005</Tax_Rate>
         <date>2011-07-01</date>
         <Tax_Rate taxtype="Local City Withholding (Work)">0</Tax_Rate>
         <date>2011-07-01</date>
      </Tax_Code>
   </row>
   <row>
      <Tax_Code Code="42010033">
         <Tax_Rate taxtype="Local City Withholding (Resident)">0.005</Tax_Rate>
         <date>2011-07-01</date>
         <Tax_Rate taxtype="Local City Withholding (Work)">0.01</Tax_Rate>
         <date>2012-07-01</date>
         <Tax_Rate taxtype="xxxx">0.01</Tax_Rate>
         <date>2012-07-01</date>
      </Tax_Code>
   </row>
</root>

As you can see, the output doesn't have the most recent tax rate for tax code 42530035. Even if I try to add position() = 1, I still don't get the most entry. I can't find an explanation of why the sorting doesn't work.

Upvotes: 1

Views: 716

Answers (2)

Mads Hansen
Mads Hansen

Reputation: 66781

The items in your inner-most xsl:for-each-group are returned in document order. If you want to process the first sorted item, you could use an xsl:for-each and xsl:sort the current-group() items, and then only process the first one:

<xsl:for-each-group select="current-group()" group-by="Tax">                        
  <xsl:for-each select="current-group()">
    <xsl:sort order="descending" select="Start_Date" />
    <xsl:if test="position() = 1">
      <xsl:apply-templates select="." mode="Tax_Code"/>
    </xsl:if>    
  </xsl:for-each>
</xsl:for-each-group>  

Complete stylesheet:

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

    <xsl:template match="/">
        <root>
            <xsl:for-each-group select="*/row" 
                group-by="Tax_Code">
                <xsl:sort order="descending" select="Start_Date"/>
                <row>
                    <Tax_Code>
                        <xsl:attribute name="Code" select="current-grouping-key()"/>
                        <xsl:for-each-group select="current-group()" group-by="Tax">

                            <xsl:for-each select="current-group()">
                                <xsl:sort order="descending" select="Start_Date" />
                                <xsl:if test="position() = 1">
                                    <xsl:apply-templates select="." mode="Tax_Code"/>
                                </xsl:if>    
                            </xsl:for-each>

                        </xsl:for-each-group>  
                    </Tax_Code>
                </row>
            </xsl:for-each-group>
        </root>
    </xsl:template>

    <xsl:template name="Tax_Code" match="row" mode="Tax_Code">
        <Tax_Rate>
            <xsl:attribute name="taxtype">
                <xsl:value-of select="Tax"/>
            </xsl:attribute>
            <xsl:value-of select="Tax_Rate"/>
        </Tax_Rate>
        <date>
            <xsl:value-of select="Start_Date"/>
        </date>      
    </xsl:template>

</xsl:stylesheet>

Upvotes: 2

Martin Honnen
Martin Honnen

Reputation: 167716

Use

   <date>
        <xsl:value-of select="max(current-group()/xs:date(Start_Date))"></xsl:value-of>
    </date>

to output the highest date in the group you have. The ordering with the xsl:sort you have only applied in the outer grouping and only for the groups, in the inner group you currently output the date of the first item in the group (in the original document order).

Upvotes: 2

Related Questions