Search code examples
xsltxslt-2.0xslt-grouping

Grouping of variables in XSLT 2.0


For each <LineDetail> node that has the same value of <CtryCd> and populates 1 <LineDetail> with the summation of <Amt>. But, I need to check also the <SI> and <TI> node. The output tag of the summation will be based from the <SI> and <TI> node. The <Amt1> will generate if the value of <SI> and <TI> are both false, while <Amt2> will generate if the value of <SI> is true and <TI> is false. I created an XSLT and already got the summation. But there's still something missing in my output.

This is a sample XML:

<Record>
<Data>
    <Process>
        <Header>
            <ID>22-BBB</ID>
            <Date>2017-02-14</Date>
            <ContactName>Abegail</ContactName>
            <!-- some other elements -->
        </Header>
        <Detail>
            <ID>22-CCC</ID>
            <RequestedDate>2017-02-14</RequestedDate>
            <!-- some other elements -->
            <LineDetail>
                <CtryCd>AF</CtryCd>
                <SI>false</SI>
                <TI>false</TI>
                <Amt>11.11</Amt>
            </LineDetail>
            <LineDetail>
                <CtryCd>SE</CtryCd>
                <SI>true</SI>
                <TI>false</TI>
                <Amt>22.22</Amt>
            </LineDetail>
            <LineDetail>
                <CtryCd>AF</CtryCd>
                <SI>false</SI>
                <TI>false</TI>
                <Amt>33.33</Amt>
            </LineDetail>
            <LineDetail>
                <CtryCd>AF</CtryCd>
                <SI>true</SI>
                <TI>false</TI>
                <Amt>55.55</Amt>
            </LineDetail>       
        </Detail>
    </Process>
</Data>
</Record>

Generated output:

<Record>
<Data>
    <Process>
        <Header>
            <ID>22-BBB</ID>
            <Date>2017-02-14</Date>
            <ContactName>Abegail</ContactName>
            <!-- some other elements -->
        </Header>
        <Detail>
            <LineDetail>
                <CtryCd>AF</CtryCd>
                <Amt1>99.99</Amt1>
            </LineDetail>
        </Detail>
        <Detail>
            <LineDetail>
                <C<Amt2>22.22</Amt2>
            </LineDetail>
        </Detail>
    </Process>
</Data>
</Record>

Expected output:

<Record>
<Data>
    <Process>
        <Header>
            <ID>22-BBB</ID>
            <Date>2017-02-14</Date>
            <ContactName>Abegail</ContactName>
            <!-- some other elements -->
        </Header>
        <Detail>
            <ID>22-CCC</ID>
            <RequestedDate>2017-02-14</RequestedDate>
            <!-- some other elements -->
            <LineDetail>
                <CtryCd>AF</CtryCd>
                <Amt1>44.44</<Amt1>
                <Amt2>55.55</Amt2>
            </LineDetail>
            <LineDetail>
                <CtryCd>SE</CtryCd>
                <Amt1>0</<Amt1>
                <Amt2>22.22</Amt2>
            </LineDetail>           
        </Detail>
    </Process>
</Data>
</Record>

XSLT:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="CtryCd" match="LineDetail" use="CtryCd"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="Detail">
    <xsl:for-each-group select="LineDetail" group-by="CtryCd">
        <Detail>
            <LineDetail>
                <CtryCd>
                    <xsl:value-of select="CtryCd"/>
                </CtryCd>
                <xsl:if test="lower-case(SI)='false' and lower-case(TI)='false'">
                    <Amt1>
                        <xsl:value-of select="sum(current-group()/Amt)"/>
                    </Amt1>
                <xsl:if test="lower-case(SI)='true' and lower-case(TI)='false'">
                    <Amt2>
                        <xsl:value-of select="sum(current-group()/Amt)"/>
                    </Amt2>
                </xsl:if>
                </xsl:if>
                <xsl:if test="lower-case(SI)='true' and lower-case(TI)='false'">
                    <Amt2>
                        <xsl:value-of select="sum(current-group()/Amt)"/>
                    </Amt2>
                </xsl:if>
            </LineDetail>
        </Detail>
    </xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Solution

  • You current if condition will only check the first item in the group. You really need it to be part of the actual sum statement, as a condition on each node on the group.

    <Amt1>
        <xsl:value-of select="sum(current-group()[lower-case(SI)='false' and lower-case(TI)='false']/Amt)"/>
    </Amt1>
    

    You would then have a similar statement for the Amt2, testing for "true" and "false"

    Try this XSLT

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:key name="CtryCd" match="LineDetail" use="CtryCd"/>
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Detail">
        <xsl:for-each-group select="LineDetail" group-by="CtryCd">
            <Detail>
                <LineDetail>
                    <CtryCd>
                        <xsl:value-of select="CtryCd"/>
                    </CtryCd>
                    <Amt1>
                        <xsl:value-of select="sum(current-group()[lower-case(SI)='false' and lower-case(TI)='false']/Amt)"/>
                    </Amt1>
                    <Amt2>
                        <xsl:value-of select="sum(current-group()[lower-case(SI)='true' and lower-case(TI)='false']/Amt)"/>
                    </Amt2>
                </LineDetail>
            </Detail>
        </xsl:for-each-group>
    </xsl:template>
    </xsl:stylesheet>