I have searched all around to find a solution to my problem, but i just got more questions...
consider the following XML:
From http://www.w3.org/TR/xslt#named-templates
The value of the
name
attribute is a QName, which is expanded as described in [2.4 Qualified Names].
It means that is neither an expression nor an AVT.
Explicit xsl:call-template
instructions are fine whether by logic instructions or pattern matching like:
<xsl:template match="dyn[@id='name1']" mode="dynamic">
<xsl:call-template name="name1"/>
</xsl:template>
Another approach is named template references...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vTemplate" select="document('')/*/xsl:template"/>
<xsl:template match="dyn">
<xsl:apply-templates select="$vTemplate[@name = current()/@id]"/>
</xsl:template>
<xsl:template match="xsl:template[@name='name1']"
name="name1"> one </xsl:template>
<xsl:template match="xsl:template[@name='name2']"
name="name2"> two </xsl:template>
<xsl:template match="xsl:template[@name='name3']"
name="name3"> three </xsl:template>
<xsl:template match="xsl:template[@name='name4']"
name="name4"> four </xsl:template>
</xsl:stylesheet>
Output:
one two three four
document('')
to process the XSLT rather than the original XML file, the original document being processed is not available in the named templates. However, you can explicitly pass current()
as parameter to the templates if needed:
<xsl:template match="dyn">
<xsl:apply-templates select="$vTemplate[@name = current()/@id]">
<xsl:with-param name="current" select="current()"/>
</xsl:apply-templates>
</xsl:template>
If needed, $current
can be used to access the original document:
<xsl:template match="xsl:template[@name='name1']" name="name1">
<xsl:param name="current"/>
<xsl:value-of select="$current/@id"/>
<xsl:text> becomes one</xsl:text>
</xsl:template>
If needed, $current
could be re-established as current node using for-each
:
<xsl:template match="xsl:template[@name='name2']" name="name2">
<xsl:param name="current"/>
<xsl:for-each select="$current">
<xsl:value-of select="@id"/>
<xsl:text> becomes two</xsl:text>
</xsl:for-each>
</xsl:template>
Usually when someone tries to do this, it means they are not aware of the full power of xsl:apply-templates. The way to do dynamic despatch in XSLT is to use xsl:apply-templates. For example, for the problem cited, write template rules such as
<xsl:template match="dyn[@id='name1']">...</xsl:template>
<xsl:template match="dyn[@id='name2']">...</xsl:template>
<xsl:template match="dyn[@id='name3']">...</xsl:template>
and then use <xsl:apply-templates select="dyn"/>
to do the despatch.
If you are using the old Saxon-B or newer Saxon-PE or Saxon-EE as XSLT processor, you can use a saxon extension to achieve dynamic template calls:
<xsl:variable name="templateName">
<xsl:value-of select="dyn/@id"/>
</xsl:variable>
<saxon:call-template name="{$templateName}"/>
Don't forget to declare the saxon-Namespace in xsl-stylesheet element:
<xsl:stylesheet xmlns:saxon="http://saxon.sf.net/" [...] >
If you have only a finite number of possible templates, would it work to use an xsl if construct to select what to do in the master template?
<xsl:if test="$templateName = 'name1'">
<xsl:call-template name="name1"/>
</xsl:if>
What you're attempting isn't directly possible, but why not simply match on the id
attribute's value in the first place? If you absolutely need the indirection of the called template, then call it from the template that matches by id
(see name4
below):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<t><xsl:apply-templates select="dynamicStuff/dyn"/></t>
</xsl:template>
<!-- match on attribute value -->
<xsl:template match="dyn[@id='name1']">name1</xsl:template>
<xsl:template match="dyn[@id='name2']">name2</xsl:template>
<xsl:template match="dyn[@id='name3']">name3</xsl:template>
<xsl:template match="dyn[@id='name4']">
<xsl:call-template name="name4"/>
</xsl:template>
<!-- named templates -->
<xsl:template name="name1">name1</xsl:template>
<xsl:template name="name2">name2</xsl:template>
<xsl:template name="name3">name3</xsl:template>
<xsl:template name="name4">name4</xsl:template>
</xsl:stylesheet>
Input:
<dynamicStuff>
<dyn id="name1">...</dyn>
<dyn id="name2">...</dyn>
<dyn id="name3">...</dyn>
<dyn id="name4">...</dyn>
</dynamicStuff>
Output:
<t>name1name2name3name4</t>