How to get the sum() of node values in XSLT

40,581

First of all, your input document contains a default namespace:

<container xmlns="http://www.gtech.com/lsp/2009-09-23">

that you need to take into account, i.e. redeclare in your stylesheet and prefix elements when referring to the input document.

Then, there is absolutely no need to construct a variable when you can directly sum the nodes you are interested in - but some of the CashPaymentsToday elements do not contain a number:

<CashPaymentsToday>NONQUAL</CashPaymentsToday>

You need to exclude those elements from the sum. Finally, what you are currently doing is not possible in XSLT 1.0 - because the sum() function cannot take a so-called result tree fragment as an argument. Your approach should not "only return html", it should actually fail.

XSLT Stylesheet

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gt="http://www.gtech.com/lsp/2009-09-23"
exclude-result-prefixes="gt">

    <xsl:template match="/">
    <html>
        <body>
            <xsl:value-of select="sum(//gt:CashPaymentsToday[. != 'NONQUAL'])"/>
        </body>     
    </html>
    </xsl:template>
</xsl:stylesheet>

HTML Output

<html>
   <body>310</body>
</html>
Share:
40,581
Admin
Author by

Admin

Updated on August 09, 2022

Comments

  • Admin
    Admin almost 2 years

    I am trying to calculate the sum of numeric nodes. The following code doesn't return anything except the HTML. I know that sum() takes a nodeset. I think I have created the variable correctly. What am I doing wrong.

    Thanks.

    XML Example:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <container xmlns="http://www.gtech.com/lsp/2009-09-23">
        <Root>
            <Incentives>
                <Date>2015-03-14</Date>
                <ID>507200</ID>
                <ProgramName>Retailer Cash Incentive 16</ProgramName>
                <Retailer>
                    <ID>507201</ID>
                    <Name>Acme #2102</Name>
                    <Detail>
                        <CashPaymentsToday>50.00</CashPaymentsToday>
                        <Potential>0</Potential>
                    </Detail>
                </Retailer>
                <Retailer>
                    <ID>507202</ID>
                    <Name>Acme #2103</Name>
                    <Detail>
                        <CashPaymentsToday>60.00</CashPaymentsToday>
                        <Potential>0</Potential>
                    </Detail>
                </Retailer>
                <Retailer>
                    <ID>507203</ID>
                    <Name>Acme #1008</Name>
                    <Detail>
                        <CashPaymentsToday>0.00</CashPaymentsToday>
                        <Potential>0</Potential>
                    </Detail>
                </Retailer>
                <Retailer>
                    <ID>507207</ID>
                    <Name>Acme #2228</Name>
                    <Detail>
                        <CashPaymentsToday>200.00</CashPaymentsToday>
                        <Potential>3</Potential>
                    </Detail>
                </Retailer>
                <Retailer>
                    <ID>598419</ID>
                    <Name>Acme NO 1071</Name>
                    <Detail>
                        <CashPaymentsToday>NONQUAL</CashPaymentsToday>
                        <Potential>NONQUAL</Potential>
                    </Detail>
                </Retailer>
                <Retailer>
                    <ID>598421</ID>
                    <Name>Acme NO 1072</Name>
                    <Detail>
                        <CashPaymentsToday>NONQUAL</CashPaymentsToday>
                        <Potential>NONQUAL</Potential>
                    </Detail>
                </Retailer>
            </Incentives>
        </Root>
    </container>
    

    XSLT:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="/">
    <html>
    <body>
            <xsl:variable name="cashPayments">
                <xsl:for-each select="//Detail">
                    <xsl:if test="CashPaymentsToday"/>
                </xsl:for-each>
            </xsl:variable>
            <xsl:value-of select="sum($cashPayments)"/>
    </body>     
    </html>
        </xsl:template>
    </xsl:stylesheet>
    
  • bjimba
    bjimba about 9 years
    If you could possibly get other non-numerics besides 'NONQUAL", you can be more generic, and just test for numeric values: <xsl:value-of select="sum(//gt:CashPaymentsToday[number(.) = .])"/>. A fuller discussion of this test can be seen at stackoverflow.com/questions/3854345/xpath-test-if-is-number
  • Mathias Müller
    Mathias Müller about 9 years
    @bjimba Yes, that would be a good approach - but it's also more opaque. Let's wait for the OP to clarify if they indeed have more values that are not numbers.