7
votes

Say I have the following XML

    <A>100</A>
    <B>200</B>
    <C>300</C>

and the following XSLT

  <TEST>
    <xsl:value-of select="A + B + C"/>
  </TEST>

It produces the following output

<TEST>600</TEST>

however, when one of the nodes is blank

    <A></A>
    <B>200</B>
    <C>300</C>

I get the following.

<TEST>NaN</TEST>

I only want to add the nodes that are valid numbers. I could do this if xsl allowed me to dynamically replace a variable value by adding to the already existing variable, but even that would be messy. I assume there is an easy way that I'm missing?

I want XSLT 1.0 answers only please.

Thanks!

4
Not a duplicate because the other is the sum of one node name, this is summing different nodes together. Plus thats xslt 2.0. I asked for 1.0.james31rock
Not an exact duplicate of course but contains a hint on how to test against empty nodes (not specific to xslt 2.0).Filburt
I already knew how to use filters how to test against empty nodes. The issue I was trying to test three different nodes at once that was giving me the problem. Tried A[.!=''] + B[.!=''] + C[.!=''] and had the same issue. got to this before i saw a better answer sum(A[.!=''] | B[.!=''] | C[.!='']). problem is mine doesn't test if its a number, plus I using three filters inside of one.james31rock

4 Answers

11
votes
  <TEST>
    <xsl:value-of select="sum((A | B | C)[number(.) = .])"/>
  </TEST>

That is, sum the subset of the elements A,B,C consisting of those whose contents can be used as numbers.

Note that number('') yields NaN, and (NaN = NaN) is false, so this will not include elements without text content.

We test for numbers as discussed at https://stackoverflow.com/a/3854389/423105

5
votes

A little variation of LarsH's answer:

sum((A|B|C)[number(.) = number(.)])

the expression in the predicate doesn't cause any type error in XPath 2.0, because both arguments of = are of the same type -- xs:double.

3
votes

There is also:

For XSLT 1.0:

sum((A|B|C)[string(number())!='NaN'])

For XSLT 2.0:

sum((A,B,C)[string(number())!='NaN'])

For interest, here is another one. It works for both XSLT 1.0 and 2.0

sum((A|B|C)[number()>-99999999])

In the above, replace -99999999 with one less than the lower bound of the possible values. This works because NaN > any-thing always returns false. This is probably the most efficient solution yet, but it may be seen as a little ugly.

For example, if you know for a fact that none of A, B or C will be negative values, you could put:

sum((A|B|C)[number()>0])

...which looks a little cleaner and is still quiet readable.

2
votes
<xsl:value-of select="sum(/root/*[self::A or self::B or self::C][.!=''])"/>

This will add values from A, B, and C under the "Root" element so long as the value isn't blank.