smogg
smogg

Reputation: 1494

XSL - sum multiplication of elements

I have a few items like this one:

<item type="goods">
    <quantity unit="pcs">172</quantity>
    <unit-price currency="PLN">420</unit-price>
    <VAT>7</VAT>
</item>

I want to print sum of the cost of this items. So what mathematically:

sum(quantity*unit-price)

How can I do that with xsl? I've tried using variables with for-each loop inside and normal valye-of. but I'm still getting some strange results (I won't add this code since it's just bad).

Upvotes: 4

Views: 7723

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

In addition to the correct answer by Kirill:

I. XPath 2.0 (XSLT 2.0) solution:

Use this single XPath 2.0 expression from any language hosting XPath 2.0:

sum(/*/*/(quantity*unit-price))

Here is a complete example using XSLT 2.0 as the host of XPath 2.0:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:sequence select="sum(/*/*/(quantity*unit-price))"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the following XML document:

<items>
    <item type="goods">
        <quantity unit="pcs">1</quantity>
        <unit-price currency="PLN">2</unit-price>
        <VAT>7</VAT>
    </item>
    <item type="goods">
        <quantity unit="pcs">10</quantity>
        <unit-price currency="PLN">20</unit-price>
        <VAT>7</VAT>
    </item>
    <item type="goods">
        <quantity unit="pcs">100</quantity>
        <unit-price currency="PLN">200</unit-price>
        <VAT>7</VAT>
    </item>
</items>

the wanted, correct result is produced:

20202

II. XSLT 1.0 solution using the transform-and-sum template/function of FXSL 1.x

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:func-transform="f f:func-transform"
exclude-result-prefixes="xsl func-transform"
>
   <xsl:import href="transform-and-sum.xsl"/>

   <!-- to be applied on testTransform-and-sum5.xml -->

   <xsl:output method="text"/>

   <func-transform:func-transform/>

    <xsl:template match="/">
      <xsl:call-template name="transform-and-sum">
        <xsl:with-param name="pFuncTransform" 
                        select="document('')/*/func-transform:*[1]"/>
        <xsl:with-param name="pList" select="/*/*"/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template match="func-transform:*" mode="f:FXSL">
      <xsl:param name="arg1" select="0"/>
      <xsl:value-of select="$arg1/quantity * $arg1/unit-price "/>
    </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the same XML document (above), the same correct result is produced:

20202

Do note: Using FXSL one doesn't need to implement explicit recursion "for the Nth time", the solution is compact and potential errors writing the recursion are eliminated altogether.

As a whole the total development time, readability and flexibility are dramatically improved.

Upvotes: 3

Kirill Polishchuk
Kirill Polishchuk

Reputation: 56162

XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/root">
    <xsl:text>sum:</xsl:text>

    <xsl:call-template name="sum">
      <xsl:with-param name="nodes" select="item"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="sum">
    <xsl:param name="nodes" />
    <xsl:param name="sum" select="0" />

    <xsl:variable name="current" select="$nodes[1]" />

    <xsl:if test="$current"> 
      <xsl:call-template name="sum">
        <xsl:with-param name="nodes" select="$nodes[position() &gt; 1]" />
        <xsl:with-param name="sum" select="$sum + $current/quantity * $current/unit-price" />
      </xsl:call-template>
    </xsl:if>

    <xsl:if test="not($current)">
      <xsl:value-of select="$sum" />
    </xsl:if>

  </xsl:template>

</xsl:stylesheet>

Input XML:

<root>
  <item type="goods">
    <quantity unit="pcs">1</quantity>
    <unit-price currency="PLN">2</unit-price>
    <VAT>7</VAT>
  </item>
  <item type="goods">
    <quantity unit="pcs">10</quantity>
    <unit-price currency="PLN">20</unit-price>
    <VAT>7</VAT>
  </item>
  <item type="goods">
    <quantity unit="pcs">100</quantity>
    <unit-price currency="PLN">200</unit-price>
    <VAT>7</VAT>
  </item>
</root>

Output:

sum:20202

Upvotes: 10

Related Questions