SreenivasBR
SreenivasBR

Reputation: 53

Sum two elements by grouping using XSLT 2.0

I'm trying to sum two elements "amount" and "retroAmount" group by "tmid" using xslt 2.0 and I tried two methods, in method-1 everything is stacking up and in the method-2 it displays NaN. Any ideas about how this can be fixed?

Here is my XML file:

<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   <row>
      <tmid>abc</tmid>
      <amount>651.03</amount>
      <retroAmount>0</retroAmount>
   </row>
   <row>
      <tmid>abc</tmid>
      <amount>250.75</amount>
      <retroAmount>-10</retroAmount>
   </row>
   <row>
      <tmid>abc</tmid>
      <amount>132</amount>
      <retroAmount>-16.1</retroAmount>
   </row>
   <row>
      <tmid>xyz</tmid>
      <amount>129.19</amount>
      <retroAmount>49.96</retroAmount>
   </row>
   <row>
      <tmid>xyz</tmid>
      <amount>148.76</amount>
      <retroAmount>0</retroAmount>
   </row>
   <row>
      <tmid>xyz</tmid>
      <amount>92.29</amount>
      <retroAmount>12</retroAmount>
   </row>
</Request>

Output I am expecting:

<top xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
     xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Results>
      <tmId>abc</tmId>
      <total>1007.68</total>
   </Results>
   <Results>
      <tmId>xyz</tmId>
      <total>432.2</total>
   </Results>
</top>

Any help is appreciated.

The XSLT code I was playing with: Method-1 (everything is stacking up or being displayed without summing)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="2.0">
    
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:template match="/">
        <root>
            <xsl:for-each-group select="Request/row"
                group-by="tmid">
                <row>
                    <tmid>
                        <xsl:value-of
                            select="current-grouping-key()"
                        />
                    </tmid>
                    <xsl:for-each-group select="current-group()" group-by=".">
                        <amount>
                            <xsl:value-of select="sum(number(current-group()/amount))"/>
                        </amount>
                        <retroamount>
                            <xsl:value-of select="sum(number(current-group()/retroAmount))"/>
                        </retroamount>
                    </xsl:for-each-group>
                </row>
            </xsl:for-each-group>
        </root>
    </xsl:template>
</xsl:stylesheet>

Method-2 (I was only using "amount" and still it is displaying NaN, I would like to sum up both "amount" and "retroAmount"

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="2.0">
    
    <xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:template match="/*">
        <top>
            <xsl:for-each-group select="//tmid" group-by=".">
                <Results>
                    <tmId>
                        <xsl:sequence
                            select="current-grouping-key()"
                        />
                    </tmId>
                    <total>
                        <xsl:sequence select="sum(number(current-group()/amount))"/>
                    </total>
                </Results>
            </xsl:for-each-group>
        </top>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 0

Views: 431

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167401

You basically want

  <xsl:template match="Request">
    <xsl:copy>
      <xsl:for-each-group select="row" group-by="tmid">
        <Results>
          <tmId>{current-grouping-key()}</tmId>
          <total>{sum(current-group()!(amount, retroAmount))}</total>
        </Results>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

(that is XSLT 3 with XPath 3.1 syntax, but in XSLT 2 with XPath 2 syntax you would use

  <xsl:template match="Request">
    <xsl:copy>
      <xsl:for-each-group select="row" group-by="tmid">
        <Results>
          <tmId>
            <xsl:value-of select="current-grouping-key()"/>
          </tmId>
          <total>
            <xsl:value-of select="sum(current-group()/(amount, retroAmount))"/>
          </total>
        </Results>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

I only later noticed that the Request element is meant to be transformed to a top element so change the <xsl:template match="Request"><xsl:copy>...</xsl:copy></xsl:template> from above suggestions to <xsl:template match="Request"><step>...</step></xsl:template>.

Upvotes: 1

Related Questions