10

I want a variable to have the value of an element if the value is numeric, but if it's not then I want the variable to have the value 0.

In other words, is there a simple equivalent of the following in XSLT?

var foobar = is_numeric(element value) ? element value : 0

Or how would you write this?

<xsl:variable name="foobar" select=" ? " />
Svish
  • 152,914
  • 173
  • 462
  • 620

3 Answers3

17

XPath 1.0:

<xsl:variable name="foobar">
  <xsl:choose>
    <xsl:when test="number($value) = number($value)">
      <xsl:value-of select="$value"/>
    </xsl:when>
    <xsl:otherwise>0</xsl:otherwise>
  </xsl:choose>
</xsl:variable>

Reference for this clever number($value) = number($value) numeric test: Dimitre Novatchev's answer to "Xpath test if is number" question.

Community
  • 1
  • 1
kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Is there any reason why this approach may fail if your variable is returning a *node set*? I ask because if I define the variable as one or the other it works. But the moment I introduce the `xsl:choose` logic it don't work right. – Andrew Truckle Aug 20 '23 at 21:02
  • https://stackoverflow.com/q/76941354/3016153 – michael.hor257k Aug 20 '23 at 22:05
10

In XPath 2.0 yes, you can use "castable as"

<xsl:variable name="foobar" as="xs:double"
   select="if (x castable as xs:double) then x else 0" />
Svish
  • 152,914
  • 173
  • 462
  • 620
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
6

You could use:

<xsl:variable name="x" 
              select="(ELEMENT[. castable as xs:double], 0)[1]"/>

or

<xsl:variable name="x" 
              select="sum(ELEMENT[. castable as xs:double])"/>
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • What does `(x, y)` do, in your first example? – Svish Oct 03 '13 at 08:03
  • 1
    @Svish, `(x, y)` forms a sequence of `x` and `y` but as `ELEMENT[. castable as xs:double]` yields the empty sequence if the content of `ELEMENT` is not a double the value is either a sequence of `(ELEMENT, 0)` or simply the sequence `(0)`. And the predicate `[1]` then selects `ELEMENT` or `0`. – Martin Honnen Oct 03 '13 at 09:02
  • @Svish so in particular if there is more than one `ELEMENT` child in the current context then my approach will always return `0` (because a sequence of more than one item is not `castable as xs:double`), whereas Michael's approach will return the first `ELEMENT` child which can be cast to `xs:double` (in the `(x, y)[1]` case) or the sum of all such `ELEMENT`s (in the `sum()` case), or `0` if none of them are appropriately castable. – Ian Roberts Oct 03 '13 at 09:09
  • Cool, thanks for the explanations. I'm going for @IanRoberts since that expresses most clearly what I actually want. Seems less hackish, hehe. – Svish Oct 04 '13 at 11:59