Xsl conversion

I am new to xsl transforms and I have a question. I iterate over xml as follows:

 <PO>
<Items>
  <Item>
     <Price>2</Price>
     <Quantity>5</Quantity>
  </Item>
  <Item>
     <Price>3</Price>
     <Quantity>2</Quantity>
  </Item>    
 </Items>
 <QuantityTotal></QuantityTotal>
 </PO>

Now I want to insert the value in the NumberTotal node:
The value is the sum of the price * the number of all items, in this case (2 * 5) + (3 * 2) = 16 How can I do this, I tried this with a loop and variables, but the variables are immutable, so I don’t know how I can achieve this.

thanks for your help

+5
source share
3 answers

Here is the XSLT solution - no extension functions required :

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

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="QuantityTotal">
  <xsl:copy>
   <xsl:call-template name="sumProducts">
     <xsl:with-param name="pNodes" select="../Items/Item"/>
   </xsl:call-template>
  </xsl:copy>
 </xsl:template>

 <xsl:template name="sumProducts">
  <xsl:param name="pNodes"/>
  <xsl:param name="pSum" select="0"/>
  <xsl:param name="pEname1" select="'Price'"/>
  <xsl:param name="pEname2" select="'Quantity'"/>

  <xsl:choose>
   <xsl:when test="not($pNodes)">
    <xsl:value-of select="$pSum"/>
   </xsl:when>
  <xsl:otherwise>
    <xsl:call-template name="sumProducts">
      <xsl:with-param name="pNodes" select=
      "$pNodes[position() > 1]"/>
      <xsl:with-param name="pSum" select=
      "$pSum
      +
       $pNodes[1]/*[name()=$pEname1]
      *
       $pNodes[1]/*[name()=$pEname2]
       "/>
    </xsl:call-template>
  </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

when this conversion is applied to the provided XML document :

<PO>
    <Items>
        <Item>
            <Price>2</Price>
            <Quantity>5</Quantity>
        </Item>
        <Item>
            <Price>3</Price>
            <Quantity>2</Quantity>
        </Item>
    </Items>
    <QuantityTotal></QuantityTotal>
</PO>

the desired result is obtained :

<PO>
   <Items>
      <Item>
         <Price>2</Price>
         <Quantity>5</Quantity>
      </Item>
      <Item>
         <Price>3</Price>
         <Quantity>2</Quantity>
      </Item>
   </Items>
   <QuantityTotal>16</QuantityTotal>
</PO>
+4

Dimitre, :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="../Items/Item[1]" mode="sum"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Item" mode="sum">
        <xsl:param name="pSum" select="0"/>
        <xsl:variable name="vNext" select="following-sibling::Item[1]"/>
        <xsl:variable name="vSum" select="$pSum + Price * Quantity"/>
        <xsl:apply-templates select="$vNext" mode="sum">
            <xsl:with-param name="pSum" select="$vSum"/>
        </xsl:apply-templates>
        <xsl:if test="not($vNext)">
            <xsl:value-of select="$vSum"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

:

<PO>
    <Items>
        <Item>
            <Price>2</Price>
            <Quantity>5</Quantity>
        </Item>
        <Item>
            <Price>3</Price>
            <Quantity>2</Quantity>
        </Item>
    </Items>
    <QuantityTotal>16</QuantityTotal>
</PO>
+2

XSLT2, node - - . XSLT1 node -set.

:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:variable name="extendedItems" as="xs:integer*">
        <xsl:for-each select="//Item">
             <xsl:value-of select="./Price * ./Quantity"/>
        </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="total">
        <xsl:value-of select="sum($extendedItems)"/>
    </xsl:variable>

    <xsl:template match="//QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="$total"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

" " NumberTotal. , QuantityTotal . , . NumberTotal node.

The key to understanding XSL is that it is declarative in nature. The most common conceptual error made by almost all beginners is that the stylesheet is a sequential program that processes the input XML document. This is actually the opposite. The XSL engine reads an XML document. and for each new tag he encounters, he scans the stylesheet for a “better” match, executing this template.

EDIT:

Here is the xslt1.1 version that works with Saxon 6.5

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ex="http://exslt.org/common"
    extension-element-prefixes="ex"
    version="1.1">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:variable name="extendedItems">
        <xsl:for-each select="//Item">
            <extended>
             <xsl:value-of select="./Price * ./Quantity"/>
            </extended>
            </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="total">
        <xsl:value-of select="sum(ex:node-set($extendedItems/extended))"/>
    </xsl:variable>
    <xsl:template match="//QuantityTotal">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="$total"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
+1
source

All Articles