Search code examples
sortingxsltxslt-2.0cross-reference

XSLT 2.0 Sort by variable from another style sheet


I need to create XML that is sorted by numeric values I pull from another XSLT, which I use as a cross reference.
The below source XML (source.xml) has four alpha characters at Partner/Header/@whse.

<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
    <Header whse="NCCH" >
        <Contract claimNumber="00000000" />
    </Header>
    <Header whse="TXAU" >
        <Contract claimNumber="00000000" />
    </Header>
    <Header whse="LANO" >
        <Contract claimNumber="00000000" />
    </Header>
    <Header whse="MIGR">
        <Contract claimNumber="00000000" />
    </Header>
    <Header whse="TXHO">
        <Contract claimNumber="00000000" />
    </Header>
</Partner>

I need to cross reference the alpha characters to get the DUNS+4.
I use this XSLT (Duns_config.xslt) to get the DUNS.

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

<xsl:template name="SHIPTODUNS">
    <xsl:param name="Whse" />
    <xsl:choose>
        <xsl:when test="$Whse = 'LANO'"><xsl:value-of select="'0044893600101'" /></xsl:when>
        <xsl:when test="$Whse = 'TXHO'"><xsl:value-of select="'0044893600103'" /></xsl:when>
        <xsl:when test="$Whse = 'TXAU'"><xsl:value-of select="'0044893600105'" /></xsl:when>
        <xsl:when test="$Whse = 'NCCH'"><xsl:value-of select="'0044893600214'" /></xsl:when>
        <xsl:when test="$Whse = 'MIGR'"><xsl:value-of select="'8949713340601'" /></xsl:when>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

In the main XSLT (Transaction.xslt), I include the Duns_config.xslt and call SHIPTODUNS putting the data in the variable $headerDuns.
I then get the last three digits of the DUNS+4 and put them into the variable $varWhse and try to sort by this variable:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:include href="Duns_config.xslt"/> 
    <xsl:template match="Partner">
        <Partner partnerId="{./@partnerId}">
            <xsl:apply-templates select="./Header" />
        </Partner>
    </xsl:template>

    <xsl:template match="Header">
        <xsl:variable name="headerDuns">
            <xsl:call-template name = "SHIPTODUNS">
                <xsl:with-param name="Whse" select="./@whse" />
            </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="varWhse">           
            <xsl:value-of select="substring($headerDuns, 11, 3)" />
        </xsl:variable>
        <xsl:for-each select="current()">
        <xsl:sort select="$varWhse" />
        <transaction varwhse="{$varWhse}">
            <duns number="{$headerDuns}" />
        </transaction>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

The output is not sorted by the $varWhse:

<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
   <transaction varwhse="214">
      <duns number="0044893600214"/>
   </transaction>
   <transaction varwhse="105">
      <duns number="0044893600105"/>
   </transaction>
   <transaction varwhse="101">
      <duns number="0044893600101"/>
   </transaction>
   <transaction varwhse="601">
      <duns number="8949713340601"/>
   </transaction>
   <transaction varwhse="103">
      <duns number="0044893600103"/>
   </transaction>
</Partner>

I am wanting the data to come out like this:

<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
   <transaction varwhse="101">
      <duns number="0044893600101"/>
   </transaction>
   <transaction varwhse="103">
      <duns number="0044893600103"/>
   </transaction>
   <transaction varwhse="105">
      <duns number="0044893600105"/>
   </transaction>
   <transaction varwhse="214">
      <duns number="0044893600214"/>
   </transaction>
   <transaction varwhse="601">
      <duns number="8949713340601"/>
   </transaction>
</Partner>

Anyone see what I am doing wrong or have another way?
This is my first post on this site. It's a lot of information and I hope it makes sense.


Solution

  • One of the problems with your code is that the xsl:sort in your Header template is not the first instruction; xsl:sort has to be the first instruction. Another problem is that the sorting appears in the Header template, but it would have to be applied on an upper level. To correct these mistakes, some restructuring of your templates has to be performed.

    For the following solution I chose to put all of the logic into one template - with two steps:

    1. Fill a variable named mapping with a sub-data-structure with item elements.
    2. Iterate over that variable with a sorted xsl:for-each and output the transaction elements.

    The result looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
        <xsl:include href="Duns_config.xslt"/> 
    
        <xsl:template match="Partner">
            <Partner partnerId="{./@partnerId}">
                <xsl:variable name="mapping">
                    <xsl:for-each select="Header">
                        <xsl:variable name="headerDuns">
                            <xsl:call-template name = "SHIPTODUNS">
                                <xsl:with-param name="Whse" select="@whse" />
                            </xsl:call-template>
                        </xsl:variable>
                        <xsl:variable name="varWhse">           
                            <xsl:value-of select="substring($headerDuns, 11, 3)" />
                        </xsl:variable>
                        <item>
                            <whse><xsl:value-of select="$varWhse" /></whse>
                            <duns><xsl:value-of select="$headerDuns" /></duns>
                        </item>
                    </xsl:for-each>
                </xsl:variable>
                <xsl:for-each select="$mapping/item">
                    <xsl:sort select="whse" />        <!-- See how xsl:sort is the first instruction -->
                    <transaction varwhse="{whse}">
                        <duns number="{duns}"/>
                    </transaction>
                </xsl:for-each>
            </Partner>
        </xsl:template>
    
    </xsl:stylesheet>
    

    And its output is:

    <?xml version="1.0" encoding="UTF-8"?>
    <Partner partnerId="TradingPartner1">
       <transaction varwhse="101">
          <duns number="0044893600101"/>
       </transaction>
       <transaction varwhse="103">
          <duns number="0044893600103"/>
       </transaction>
       <transaction varwhse="105">
          <duns number="0044893600105"/>
       </transaction>
       <transaction varwhse="214">
          <duns number="0044893600214"/>
       </transaction>
       <transaction varwhse="601">
          <duns number="8949713340601"/>
       </transaction>
    </Partner>
    

    Which is as desired.