Search code examples
xsltxslt-2.0

Not possible to use template parameter in the match XPath expression in XSLT 2.0?


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!?


Solution

  • 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');