1

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?

Hash
  • 7,726
  • 9
  • 34
  • 53
Steve Awsd
  • 55
  • 2
  • 5

2 Answers2

2

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>
Community
  • 1
  • 1
kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • With safari and firefox doesn't work. Using XPATH in Oxygen Editor it works so I don't know why in this browsers don't work! I need them to work in this browsers. – Steve Awsd Oct 28 '13 at 22:47
  • Tells me that the expression to be evaluated is invalid. – Steve Awsd Oct 28 '13 at 22:52
  • Browsers do generally not support XSLT 2.0, and chances are they never will. Run your template on the server. XSLT is not for the client side. – Tomalak Oct 28 '13 at 22:57
  • I'm confused because if I have a tag number instead of score (example: 5) and do "" it works .. why this would not work? – Steve Awsd Oct 28 '13 at 23:07
  • @SteveAwsd that's because your simplified case, without the `number()` function after the final `/`, is ok in XPath 1.0. You need XPath 2.0 to support a function after the final `/`. – kjhughes Oct 28 '13 at 23:29
  • @MichaelKay First comment on this answer. – Tomalak Oct 29 '13 at 09:05
  • @MichaelKay, I transformed his XSLT into the esp namespace, and the answer was clear. ...or, my initial "works here" answer elicited a not-in-Safari/Firefox comment. One of those two. ;-) – kjhughes Oct 29 '13 at 11:48
1

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.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164