开发者

XSLT call-template with dynamic QName?

I have searched all around to find a solution to my problem, but i just got more questions...

consider the following XML:

<dynamicStuff>
      <dyn id="name1">...</dyn>
      <dyn id="name2">...</dyn>
      <dyn id="name3">...</dyn>
      <dyn id="name4">...</dyn> 
</dynamicStuff>

and suppose the I have an XSLT file as follows:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

     <xsl:template name="name1">    
         ...
     </xsl:template>

     <xsl:template name="name2">    
         ...
     </xsl:template>

     <xsl:template name="name3">    
         ...
     </xsl:template>

     <xsl:template name="name4">    
         ...
     </xsl:template>

</xsl:stylesheet>

What I want to do is from a SECOND XSLT file dynamically determine which template to call with something like this:

<xsl:variable name="templateName">
     <xsl:value-of select="dyn/@id"/>
</xsl:variable>

<xsl:call-template name="$templateName"/>

sadly its not working, believe me when I say that I have tried a lot of differen开发者_如何学Ct stuff, though it sounds so simple it does not work either...

Am I missing something?

Edit:

I have successfully done the following:

<xsl:template name="staticName">
    <xsl:param name="id" />

    <xsl:if test="$id = 'name1'">....</xsl:if>
    <xsl:if test="$id = 'name2'">....</xsl:if>
    ...
</xsl:template>

Calling in this way:

<xsl:call-template name="staticName">
     <xsl:with-param name="id" select="@id"/>
</xsl:call-template>

Needles to say how inconvenient this is... first of all my code will be bound to that staticName (imagine I need to do this call in a dozen files)... second I will have a bunch of (un)related content inside the same template when it could be more separated... a nightmare to upgrade the system u.u

It does what I want but not in the way I need...

Thanks in advance for any light on this matter!


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 


Note: Because this technique uses 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>


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>


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.


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 name4below):

<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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜