According to this SO question it should be possible to use parameters in the XPath expression for match
. However it does not seem to work if xsl:param
of a xsl:template
is to be used in the same template.
My XML file looks as follows
<?xml version="1.0" encoding="UTF-8"?>
<myRoot>
<myNode myAttribute="3">
<myChildAttribute myChildAttribute="a" />
</myNode>
<myNode myAttribute="2">
<myChildAttribute myChildAttribute="b" />
</myNode>
<myNode myAttribute="1" />
</myRoot>
and my XSL file like that.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="myRoot">
<xsl:apply-templates select="myNode">
<xsl:sort select="@myAttribute" />
<xsl:with-param name="myParam" select="max(myNode/@myAttribute)" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="myNode[node() and @myAttribute = $myParam]">
<xsl:param name="myParam" />
<xsl:for-each select="myChildAttribute">
INSERT INTO a(b) VALUES ('<xsl:value-of select="@myChildAttribute" />');
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Unfortunately when run with SAXON 9HE it ends with the following error
XPST0008: Variable myParam has not been declared (or its declaration is not in scope)
Is it not possible to use the parameter of a template in the match-XPath expression of the same template!?
Is it not possible to use the parameter of a template in the match-XPath expression of the same template!?
No, any variable/parameter in the match expression of the template must be in scope (defined/visible) when templates are selected for execution.
Because templates are XSLT-directives (defined at the global level), the only variables/parameters that are in scope (that they can see) are global - level variables/parameters.
A parameter of a template is passed to it only after the template is selected for execution -- not before that. This means that the value of this parameter doesn't exist when the template selection process is being executed.
Thus, if a non-global expression is to be used in the template selection for execution process, it needs to be supplied in the select
attribute of the corresponding xsl:apply-templates
instruction, where this expression can be evaluated -- not in the match
attribute of the template, where this expression cannot be evaluated.
To make this clear, the code below corrects the issue in the provided code:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="myRoot">
<xsl:apply-templates select="myNode[@myAttribute = max(../myNode/@myAttribute)]">
<xsl:sort select="@myAttribute" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="myNode[node()]">
<xsl:for-each select="myChildAttribute">
INSERT INTO a(b) VALUES ('<xsl:value-of select="@myChildAttribute" />');
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<myRoot>
<myNode myAttribute="3">
<myChildAttribute myChildAttribute="a" />
</myNode>
<myNode myAttribute="2">
<myChildAttribute myChildAttribute="b" />
</myNode>
<myNode myAttribute="1" />
</myRoot>
no error is produced and this is the output of the transformation (I cannot say "the correct output" because no requirements have been defined thus they cannot be verified. And I have my reservations about this code: for example the use of the <xsl:sort>
child of xsl:apply-templates
is not meaningful, because it will sort equal ( max() ) values and sorting of a sequence of equal values produces the same sequence):
INSERT INTO a(b) VALUES ('a');