Search code examples
xmlxsltxslt-2.0xpath-2.0

XSLT Number test


I am having a problem where the value of the $current variable came in as '6E144270003'. My original goal was to just test for a number, but '6E144270003' passed the number() test because it is a valid 'scientific notation' (as was pointed out to me here).

I need a valid test to allow data containing only integers (can include the decimal and minus sign) to equate to true and any other data to equate to false.

Should pass: 1234567890
Should pass: 123.45
Should pass: 123.5
Should pass: -123.45

 <xsl:if test="number($current) = number($current)">  
    <xsl:value-of select="$current"/>   
 </xsl:if>  

Solution

  • I am having a problem where the value of the $current variable came in as '6E144270003' and it failed in the Saxon 2.0 processor with an error of 'Cast failed, invalid lexical value - xs:double'.

    I cannot repro this problem.

    This transformation:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:variable name="current" select="'6E144270003'"/>
        <xsl:template match="/">
        <xsl:if test="number($current) = number($current)">
        <xsl:value-of select="$current"/>
     </xsl:if>
    
        </xsl:template>
    </xsl:stylesheet>
    

    when run with SAXON 9.0.0.4, produces:

    6E144270003
    

    I'm unsure why this happened when it is not a number and also how to correct it. Basically if it is not a number I don't want to output it

    The string "6E144270003" can be used as a number in XPath 2.0, because in XPath 2.0 the so called *scientific notation` is a valid way to represent a number.

    This is one interesting example where the behavior of XSLT 1.0 and XSLT 2.0 is different.

    UPDATE: The OP has indicated that he wants a test that will evaluate to false() if the string contains anything but digits.

    This can be achieved best by a regular expression, or even:

    translate($s, '0123456789', '') eq ''
    

    UPDATE2: The OP changed his question again!!!

    For the latest question here is the answer:

    Use:

    $s castable as xs:decimal
    

    This transformation demonstrates the correctness of this method:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xsl:output method="text"/>
    
        <xsl:template match="/">
         <xsl:sequence select=
          "for $s in ('1234567890',
                     '123.45',
                     '123.5',
                    '-123.45',
                     '6E144270003'
                     )
           return
             if($s castable as xs:decimal)
               then $s
               else ()
    
          "/>
        </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on any XML document (not used), the correct result is produced:

    1234567890 123.45 123.5 -123.45