Search code examples
xslt-2.0

XSLT: Converting text substring to integer value


So I'm working with some lines of XML like this ...

<p outputclass="Heading">Cycles 1–5</p>
<p outputclass="Heading">Cycles 6–10</p>
<p outputclass="Heading">Cycles 11–16</p>

And what I want to do is pull the numbers out of those, as integer values.

One of the ways I've tried to do this that hasn't worked is ...

<xsl:for-each select="p[@outputclass = 'Heading' and starts-with(., 'Cycles ')]">
  <xsl:variable name="cycle_begin_string" select="substring-after(.,'Cycles ') and substring-before(.,'–')"/>
  <xsl:variable name="cycle_end_string" select="substring-after(., '–')"/>
  <xsl:variable name="cycle_begin_number" select="number($cycle_begin_string)"/>
  <xsl:variable name="cycle_end_number" select="number($cycle_end_string)"/>
  <xsl:variable name="cycle_begin_integer" select="xs:integer($cycle_begin_number)"/>
  <xsl:variable name="cycle_end_integer" select="xs:integer($cycle_end_number)"/>
</xsl:for-each>

And yes, I know this could be tidier, but I deliberately broke it into multiple variables in order to troubleshoot where the problem is. Anyway, when I do this, I get an error message of: "Cannot convert double NaN to an integer"

What am I missing here? Because I know that the content being selected is definitely a number. Which I assume means I'm not converting it the right way. But I can't figure it out. Thanks.


Solution

  • Consider to post a minimal but complete example, together with details of which XSLT processor used, to allow others to reproduce the error; based on your question and your comment I have created the online example running SaxonC HE 12.3 and doing e.g.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="3.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="#all"
      expand-text="yes">
      
      <xsl:template match="root">
        <xsl:for-each select="p[@outputclass = 'Heading' and starts-with(., 'Cycles ')]">
          <xsl:variable name="cycle_end_string" select="substring-after(., '–')"/>
          <xsl:variable name="cycle_end_number" select="number($cycle_end_string)"/>
          <xsl:variable name="cycle_end_integer" select="xs:integer($cycle_end_number)"/>
          <div>{$cycle_end_integer}</div>
        </xsl:for-each>    
      </xsl:template>
    
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:mode on-no-match="shallow-copy"/>
    
      <xsl:template match="/" name="xsl:initial-template">
        <xsl:next-match/>
        <xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} at {current-dateTime()}</xsl:comment>
      </xsl:template>
    
    </xsl:stylesheet>
    

    against the input

    <root>
    <p outputclass="Heading">Cycles 1–5</p>
    <p outputclass="Heading">Cycles 6–10</p>
    <p outputclass="Heading">Cycles 11–16</p>
    </root>
    

    and it outputs no error but e.g.

    <?xml version="1.0" encoding="UTF-8"?>
    <div>5</div>
    <div>10</div>
    <div>16</div>
    <!--Run with SAXON HE 12.3 at 2023-11-25T23:08:29.498723Z-->
    

    just fine.