0
votes

I'm quite new to XSLT and have been struggling with implementing a recursive template, which goes through multiple text nodes and searches for a match. When creating a recursive template i get an error message saying "Required item type of first operand of '/' is node(); supplied value has item type xs:string". I don't know how to select the multiple nodes as nodes, instead of a string.

Objective: I have multiple test nodes like

<Chain>1 3 4 7 20 50 72 ...</Chain>

I want to iterate through these nodes to look for a matching number. When this number is found, i need to select a substring of a parent's element attribute.

Here's part of the stylesheet with the recursive template:

<xsl:template match="/l:LandXML/h:HexagonLandXML/h:Point/h:PointCode">
<xsl:variable name="id2" select="../@uniqueID"/>
  <xsl:call-template name="tests">
  <xsl:with-param name="input" select="/l:LandXML/h:HexagonLandXML/h:PlanFeature/h:CoordGeom/h:Spline/h:Chain"/>
  <xsl:with-param name="id" select="$id2"/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="tests">
<xsl:param name="id"/>
<xsl:param name="input"/>
<xsl:choose>
    <xsl:when test="substring-before($input, ' ') = $id">
        <xsl:value-of select="format-number(substring-before(substring-after($input/../@oID, '_'), '_'), '#')"/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:call-template name="tests">
            <xsl:with-param name="input" select="substring-after($input, ' ')"/>
            <xsl:with-param name="id" select="$id"/>
        </xsl:call-template>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

For a better understaning, a full XML and XSLT is here: https://xsltfiddle.liberty-development.net/94hvTzd/17

Thanks in advance for any help.

2
The error message is coming from Saxon, which is an XSLT 2.0 processor, so why are you restricting yourself to XSLT 1.0 facilities?Michael Kay

2 Answers

3
votes

So you want to find out if

<Chain>1 3 4 7 20 50 72 ...</Chain>

contains, say, 50?

There is no need for a recursive template here. XPath string functions can do that just fine. For XPath 1.0, use

//Chain[contains(concat(' ', ., ' '), concat(' ', $val, ' '))]

where $val is '50'. The concat() makes sure that there are no partial matches and that matches at the start and end are being found.

For XPath 2.0 and up, you can use tokenize().

//Chain[tokenize(., ' ') = $val]
1
votes

Your template in <xsl:with-param name="input" select="substring-after($input, ' ')"/> passes on a string value, a string value does not have a parent node, so you will have to store/pass on the original element as a separate parameter. Note that you at xsltfiddle have access (and are using it) to Saxon 9.8 HE, an XSLT 3 processor, where you might be able to avoid the hassle of using a recursive template by using e.g. tokenize(Chain, ' ')[. = $id] or similar.