Steve Awsd
Steve Awsd

Reputation: 55

Using sum with substring-before XSLT

I have a XML with several accounts and I'm trying to make the sum of several scores with the format score/max_score.

XML:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="accounts.xsl"?>

<accounts>
    <account active="yes">
        <id>1</id>
        <name>James</name>
        <score>50/100</score>
    </account>
    <account active="yes">
        <id>2</id>
        <name>Caty</name>
        <score>10/100</score>
    </account>
    <account active="yes">
        <id>3</id>
        <name>Acacia</name>
        <score>30/100</score>
    </account>
    <account active="yes">
        <id>4</id>
        <name>James</name>
        <score>50/100</score>
    </account>
    <account active="yes">
        <id>5</id>
        <name>Scoot_5</name>
        <score>40/100</score>
    </account>
</accounts>

And XSLT:

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

    <xsl:template match="/">
        <html>
            <body>

                <p>
                    <xsl:value-of select="sum(//accounts/account/score/number(substring-before(.,'/')))"/>  
                </p>

            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

However when I run the xml says it has error and does not return the sum. Why?

Upvotes: 1

Views: 1096

Answers (2)

Michael Kay
Michael Kay

Reputation: 163322

It seems that @kjhughes somehow worked out that you were using an XSLT 1.0 processor and that you were running in a web browser.

You need an XSLT 2.0 processor to run this. If you are indeed running in a web browser, consider Saxon-CE, which is currently the only 2.0 processor to run client-side.

Upvotes: 1

kjhughes
kjhughes

Reputation: 111541

The problem is that your XSLT 2.0 transformation won't work in web browsers, which only natively support XSLT 1.0.

For ways of summing over elements in XSLT 1.0, see Michael Kay's answer to XSLT 1 and sum function for general ideas. See Dimitre Novatchev's answer to Multiply 2 numbers and then sum with XSLT for some code samples. For actual support of XSLT 2.0 in web browsers, see Saxon-CE.

I like the recursive approach. Here's how it applies to your problem:

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

  <xsl:template match="/">
    <html>
      <body>
        <p>
          <xsl:call-template name="sumScores">
            <xsl:with-param name="pList" select="/accounts/account/score"/>
          </xsl:call-template>
        </p>
      </body>
    </html>
  </xsl:template>

  <xsl:template name="sumScores">
    <xsl:param name="pList"/>
    <xsl:param name="pAccum" select="0"/>
    <xsl:choose>
      <xsl:when test="$pList">
        <xsl:variable name="vHead" select="$pList[1]"/>
        <xsl:call-template name="sumScores">
          <xsl:with-param name="pList" select="$pList[position() > 1]"/>
          <xsl:with-param name="pAccum"
                          select="$pAccum + number(substring-before($vHead,'/'))"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$pAccum"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

(Credit: Derived from similar code written by Dimitre Novatchev.)

When this XSLT 1.0 transform is run with your input XML we get the desired HTML output:

<html>
   <body>
      <p>180</p>
   </body>
</html>

Upvotes: 2

Related Questions