XSLT call-template with dynamic QName?

前端 未结 5 1461
闹比i
闹比i 2020-12-31 20:13

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

consider the following XML:


               


        
相关标签:
5条回答
  • 2020-12-31 20:49

    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>
    
    0 讨论(0)
  • 2020-12-31 20:53

    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.

    0 讨论(0)
  • 2020-12-31 20:56

    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/" [...] >
    
    0 讨论(0)
  • 2020-12-31 20:56

    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>
    
    0 讨论(0)
  • 2020-12-31 21:02

    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 讨论(0)
提交回复
热议问题