Reputation: 1700
I am trying to get the sum of all item prices multiplied by their quantity purchased, for example (given the following)...
<item>
<itemPrice>10</itemPrice>
<itemQty>5</itemQty>
</item>
<item>
<itemPrice>5</itemPrice>
<itemQty>7</itemQty>
</item>
I want to get a total value of $85. I have tried the following, but instead of adding each calculated item purchase to the total variable it concatenates all the values into a string...
<xsl:template name="itemsTotal">
<xsl:variable name="total" select="0" />
<xsl:for-each select="item">
<xsl:value-of select="$total + (./itemQty * ./itemPrice)" />
</xsl:for-each>
</xsl:template>
What's the best way to get what I am looking for? Is there a modification I can make to my template?
Upvotes: 0
Views: 194
Reputation: 338386
Iteration is solved by recursion in XSLT 1.0.
<xsl:template name="itemTotal">
<xsl:param name="item" select="." />
<xsl:param name="carryOver" select="0" />
<xsl:variable name="runningTotal" select="
$carryOver + $item/itemPrice * $item/itemQty
" />
<xsl:variable name="nextItem" select="$item/following-sibling::item[1]" />
<xsl:choose>
<xsl:when test="not($nextItem)">
<xsl:value-of select="$runningTotal" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="itemTotal">
<xsl:with-param name="item" select="$nextItem" />
<xsl:with-param name="carryOver" select="$runningTotal" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
which can be called like this
<xsl:call-template name="itemTotal">
<xsl:with-param name="item" select="root/item[1]" />
</xsl:call-template>
and outputs
85
Notes
itemTotal
template is tail-recursive, so it's likely to be optimized into a loop by the XSLT processor. In this case stack overflows cannot happen even for high <item>
counts. following-sibling
). This works right-away for your example but it might have to be adapted to other inputs.node-set()
based solution, like michael's answer shows.Upvotes: 1
Reputation: 117140
Assuming you are using XSLT 1.0 and assuming that your processor supports the EXSLT node-set() function (which it almost certainly does) and assuming we add a root element to your XML input example and assuming you actually want the result to be 10*5 + 5*7 which is 85, not 80, try the following stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<!-- ... -->
<xsl:variable name="extPrices">
<xsl:for-each select="items/item">
<extPrice>
<xsl:value-of select="itemPrice*itemQty" />
</extPrice>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="subTotal" select="sum(exsl:node-set($extPrices)/extPrice)" />
<subTotal><xsl:value-of select="$subTotal" /></subTotal>
<!-- ... -->
</xsl:template>
</xsl:stylesheet>
Applied to the corrected input:
<items>
<item>
<itemPrice>10</itemPrice>
<itemQty>5</itemQty>
</item>
<item>
<itemPrice>5</itemPrice>
<itemQty>7</itemQty>
</item>
</items>
the result is:
<?xml version="1.0" encoding="utf-8"?>
<subTotal>85</subTotal>
Upvotes: 1