tags:

views:

152

answers:

2

Hello, I have some output from 3rd party software:

Sample XML from the software:

<testsuite name="XYZ">  
    <testcase name="ABC" status="0" time="12.001">Some stuff</testcase>  
    <testcase name="DEF" status="0" time="12,345.001">Some stuff</testcase>  
    <testcase name="GHI" status="0" time="4,321.001">Some stuff</testcase>     
</testsuite>

I need to write an XSLT that transforms this into the following:

<testsuite name="XYZ" time="16678.003">
    <testcase name="ABC" time="12.001">Some stuff</testcase>
    <testcase name="DEF" time="12,345.001">Some stuff</testcase>
    <testcase name="GHI" time="4,321.001">Some stuff</testcase>    
</testsuite>

And I have almost got there, with the exception of the time attribute of the testsuite element. Instead of getting the total, I am getting NaN. The XPath expression I am using to get this is sum(//testsuite/@time)

Note that the error does not occur when there all the times are < 1000. This is probably because XSLT doesn't parse the number when it encounters commas. (I cannot get rid of these commas from the input because it comes from 3rd party software.)

So how do I sum these values for time? Is there away to modify sum(//testsuite/@time) such that it is able to strip commas on the fly?

Thanks!

+4  A: 

Use XSLT's translate function like this:

sum(translate(//testsuite/@time,',',''))

Andrew Hare
Have you actually tried this out? This does not work work for me! The sum function only takes a parameter that is a node-set.
bojangle
+1  A: 

Use this recursive template to add all the time attributes together:

<xsl:template name="sumoftime">
  <xsl:param name="node"/>
  <xsl:param name="sum" />
  <xsl:choose>
    <xsl:when test="$node">
      <xsl:call-template name="sumoftime">
        <xsl:with-param name="node" select="$node/following-sibling::testcase[1]"/>
        <xsl:with-param name="sum" select="$sum + number(translate($node/@time, ',', ''))"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$sum"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

And then in your testsuite element, make a call to the above template

<xsl:element name="testsuite">

            ...

 <xsl:attribute name="{'time'}">
  <xsl:call-template name="sumoftime">
   <xsl:with-param name="node" select="testcase[1]"/>
   <xsl:with-param name="sum" select="0" />
  </xsl:call-template>
 </xsl:attribute>
</xsl:element>

Rather complicated, but if you're restricted to XSLT 1.0, I reckon this'd be the way to go.

Credit for the template should go to Jelovirt, as it is merely a slight mod from his answer to another question: stackoverflow.com/questions/647991/summing-numbers-with-comma-as-decimal-separator-in-xslt

bguiz
this works for me!!
bojangle