Replace unnessary characters in xml and sum them

769 views Asked by At

I'm new to XSLT, I need to add all the price values. But the price values in XML have '-' and also no values and while doing sum I'm getting NaN. So i tried the below XSL, but still I'm unable to achieve my result.

Below is a sample XML

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Edited by XMLSpy® -->
<root>
  <qwe>
    <value>+1</value>
  </qwe>
  <qwe>
    <value>20</value>
  </qwe>
  <qwe>
    <value>-</value>
  </qwe>
  <qwe>
    <value>30</value>
  </qwe>
  <qwe>
    <value>-0</value>
  </qwe>
  <qwe>
    <value/>
  </qwe>
  <qwe>
    <value>8</value>
  </qwe>
</root>

This is the XSL I'm trying to use:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" >
<xsl:template match="/">
<html>
<body>
<h2>My qwe Collection</h2>
<xsl:for-each select="root/qwe">
<xsl:variable name="len" select="replace(value,'-','0')" />
<xsl:variable name ="asd" select="sum(//value[number(.)=number(.)])"/>
sum: <xsl:value-of select="$asd"/><br/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Expected Result:

sum:59
2

There are 2 answers

0
Tim C On BEST ANSWER

I am slightly confused, because your existing sum expression should work (the only issue being you have it inside the xsl:for-each loop rather than after it).

<xsl:value-of select="sum(//value[number(.)=number(.)])"/>

Although having said that, I tried two different XSLT processors, and one returned 58 (presumably because "+1" fails the number check).

But you are using XSLT 2.0 (hopefully), then this expression should be work, and be more robust

<xsl:value-of select="sum(//value
                            [number(translate(.,'-+','0')) = number(translate(.,'-+','0'))]
                            /xs:decimal(translate(.,'-+','0')))"/>

The "translate" here will replace - with 0 and remove + entirely.

Note you will have to declare the namespace for "xs" as "http://www.w3.org/2001/XMLSchema"

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" 
     xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:template match="/">
<html>
<body>
sum: <xsl:value-of select="sum(//value
                                [number(translate(.,'-+','0')) = number(translate(.,'-+','0'))]
                                /xs:decimal(translate(.,'-+','0')))"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
3
Mathias Müller On

This is how you can do it. You were on the right track, using two variables is a good approach.

Although you make replacements in your stylesheet and store them in the variable $len - you do not make use of this variable. This does not affect the actual value elements in any way, i.e. no replacements are performed on them.

Note that if data you haven't shown to us contains other types of numbers (or more precisely, if your XML contains negative numbers you want to take into account), you'd have to revise the solution again.

Stylesheet:

<?xml version="1.0" encoding="utf-8"?>

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

<xsl:output method="xml" indent="yes"/>
<xsl:variable name="values" select="//value"/>         

<xsl:template match="/">
     <xsl:variable name="summands">
       <xsl:for-each select="$values">
          <xsl:element name="summand">
             <xsl:choose>
                <xsl:when test="contains(.,'-') or not(./text())">
                      <xsl:value-of select="0"/>
                </xsl:when>
                <xsl:when test="contains(.,'+')">
                   <xsl:value-of select="substring-after(.,'+')"/>
                </xsl:when>
                <xsl:otherwise>
                      <xsl:value-of select="."/>
                </xsl:otherwise>
             </xsl:choose>
          </xsl:element>
       </xsl:for-each>
     </xsl:variable>

     <xsl:text>SUM: </xsl:text>
     <xsl:value-of select="sum($summands/*)"/>
</xsl:template>

</xsl:stylesheet>

This yields the following output:

<?xml version="1.0" encoding="UTF-8"?>SUM: 59