I am attempting to call a template recursively on a parametrized list of node types.
If I pass these parametrized values to a template, it doesn't recurse. However, if I pass in the values to the template directly, the recursion works as expected.
How can I get the recursion to work while matching against a parametrized value?
(I am using saxon version 9.9.1.6 (home edition) to apply the XSLT transformation)
Input HTML
<p>
<p>paragraph1</p>
<p>paragraph2</p>
<a>link here</a>
</p>
XSLT with direct value for template:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="container" select="p|a"/>
<xsl:template match="p|a">
Name: <xsl:value-of select="name()"/>
Value: <xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
Name: p
Value:
Name: p
Value: paragraph1
Name: p
Value: paragraph2
Name: a
Value: link here
This is working and what I would expect to happen. But when I try and pass parametrized values to the template, it matches the top-level element but does not match any child elements.
XSLT with parametrized value for template:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="container" select="p|a"/>
<xsl:template match="$container">
Name: <xsl:value-of select="name()"/>
Value: <xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
Name: p
Value:
paragraph1
paragraph2
link here
match="$variable"
is new syntax in XSLT 3.0 that matches nodes in a node-set held in a global variable. The variable holds the matching nodes, not their names.
Also, select="p|a" selects nodes in the context of the document, which is not what you want. Use select="'p|a'"
to set the variable to a string. It helps to use an as
attribute, eg. as="node()*"
or as="xs:string"
to avoid confusion as to what the variable actually is supposed to hold.
To match on the names, use match="*[local-name()=tokenize($container, '\|')]"
Alternatively you could define a static parameter and a shadow attribute:
<xsl:param name="container" select="'p|a'" static="yes"/>
<xsl:template _match="{$container}">...</xsl:template>
Or if you prefer, you could initialize the variable to the set of matching nodes like this:
<xsl:param name="matching-nodes" select="//p | //a"/>
and then match using
<xsl:template match="$matching-nodes"/>
But note this only works if you're matching nodes in the primary source document.
According to xsltfunctions.com/xsl/fn_tokenize.html, the '|' regex needs to be escaped with a backslash. With that change this worked for me. Thanks.