How to call named templates based on a variable?

后端 未结 6 488
一生所求
一生所求 2020-12-15 23:53

I don\'t know if it\'s possible, but I\'m wondering how to do it...

Let\'s say we have the following XSL:


  Bla bla         


        
相关标签:
6条回答
  • 2020-12-15 23:59

    I think I had more or less the same problem as you. I had an "outer" template and wanted to call a different "inner" template depending on some variable set at run-time. I found your question by Googling for a way to have a dynamic <xsl:call-template>. I solved it by using <xsl:apply-templates> instead, as follows.

    The input XML (generated at run-time) contains something along the lines of:

    <template name="template_name_1"/>
    

    The XSL in the "outer" template has:

    <xsl:apply-templates select="template"/>
    

    (The select="template" in this apply-templates refers to the <template> tag in the input XML)

    And finally the "inner" template, which I wanted to include as a result of the value of the name attribute in my XML, looks like:

    <xsl:template match="template[@name='template_name_1']">
        <!-- XSL / XHTML goes here -->
    </xsl:template>
    

    (Again, the match="template[@name='xyz']" refers to the previous select="template" and in turn to the <template> tag and its name attribute in the input XML)

    In this way I can have a dynamically "called" template simply by controlled from my input XML.

    This might not be the same problem as you are trying to solve but I think it's pretty close, and much simpler than the FSXL solutions mentioned elsewhere on this page.

    0 讨论(0)
  • 2020-12-16 00:02

    What about this one?:

      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
          <xsl:template match="xsl:template[@name='foo']" name="foo">
        Bla bla bla foo
          </xsl:template>
    
          <xsl:template match="xsl:template[@name='bar']" name="bar">
        Bla bla bla bar
          </xsl:template>
    
          <xsl:template match="/">
            <xsl:variable name="templateName" select="'bar'"/>
            <xsl:apply-templates select="document('')/*/xsl:template[@name=$templateName]"/>
            <xsl:apply-templates select="document('')/*/xsl:template[@name='foo']"/>
          </xsl:template>
    
        </xsl:stylesheet>
    

    You can simplify a "call" of the template using a variable similarly as described in an earlier contribution:

    <xsl:variable name="templates" select="document('')/*/xsl:template"/> 
    
    <xsl:apply-templates select="$templates[@name=$templateName]"/>
    <xsl:apply-templates select="$templates[@name='foo']"/>
    

    Note that optional <xsl:with-param> can be used as usual.

    0 讨论(0)
  • 2020-12-16 00:06

    For anyone's future reference:

    Here is a working example based on Pavel Minaev's answer. No original thought on my part. ;-) I switched it to use msxml:node-set as he described (more or less) so that it works in .NET.

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
        <xsl:variable name="templates">
            <templates>
                <foo />
                <bar />
            </templates>
        </xsl:variable>
        <xsl:template name="foo" match="foo" mode="call-template">
            <FooElement />
        </xsl:template>
        <xsl:template name="bar" match="bar" mode="call-template">
            <BarElement />
        </xsl:template>
        <xsl:template match="/">
            <Root>
                <xsl:variable name="template-name">bar</xsl:variable> <!-- Change this to foo to get the other template. -->
                <xsl:apply-templates select="msxsl:node-set($templates)/*/*[local-name() = $template-name]" mode="call-template" />
            </Root>
        </xsl:template>
    </xsl:stylesheet>
    
    0 讨论(0)
  • 2020-12-16 00:09

    No, this is not possible not directly possible. The calling convention is:

    <xsl:call-template name="QName" />
    

    Where a QName is defined as:

    QName ::= PrefixedName | UnprefixedName
    
    PrefixedName   ::= Prefix ':' LocalPart
    UnprefixedName ::= LocalPart
    
    Prefix         ::= NCName
    LocalPart      ::= NCName
    

    Basically this boils down to "characters only, no expressions". As the other answers highlight, there are in fact ways to do something equivalent, but the straightforward approach/naïve approach will not work.

    0 讨论(0)
  • 2020-12-16 00:16

    Update: The links below were updated to point to web.archive.org -- unfortunately, IDEALLIANCE has made all Exteme Markup Languages conference proceedings unavailable... In due time, I will find a more permanent place for these two articles.


    This is implemented in FXSL.

    There are good explanations of the main principles of FXSL.

    See the following two articles:

    "Functional programming in XSLT using the FXSL library" (for XSLT 1.0), (PDF) at:

    http://web.archive.org/web/20070710091236/http://www.idealliance.org/papers/extreme/proceedings/xslfo-pdf/2003/Novatchev01/EML2003Novatchev01.pdf

    (HTML) at:

    http://conferences.idealliance.org/extreme/html/2003/Novatchev01/EML2003Novatchev01.html



    "Higher-Order Functional Programming with XSLT 2.0 and FXSL" (PDF) at:

    http://web.archive.org/web/20070222111927/http://www.idealliance.org/papers/extreme/proceedings/xslfo-pdf/2006/Novatchev01/EML2006Novatchev01.pdf

    (HTML) at: http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html



    Using FXSL I have been able to solve easily and elegantly many problems, that seem "impossible for XSLT". One can find a lot of examples here.

    0 讨论(0)
  • 2020-12-16 00:23

    It's not possible exactly as you describe, but if you want to be able to choose a template at run-time based on some value you set elsewhere, there is a trick to do that. The idea is to have your named template also match a node with a corresponding name in a distinct mode (so that it doesn't mess up your normal transformation), and then match on that. For example:

    <xsl:stylesheet ... xmlns:t="urn:templates">
    
      <!-- Any compliant XSLT processor must allow and ignore any elements 
           not from XSLT namespace that are immediate children of root element -->
      <t:templates>
        <t:foo/>
        <t:bar/>
      </t:templates>
    
      <!-- document('') is the executing XSLT stylesheet -->     
      <xsl:variable name="templates" select="document('')//t:templates" />
    
      <xsl:template name="foo" match="t:foo" mode="call-template">
        Bla bla bla
      </xsl:template>
    
      <xsl:template name="bar" match="t:foo" mode="call-template">
        Bla bla bla
      </xsl:template>
    
      <xsl:template match="/">
        <xsl:variable name="template-name" select="..." />
        <xsl:apply-templates select="$templates/t:*[local-name() = $template-name]"
                             mode="call-template"/>
      </xsl:template>
    

    Note that you can use <xsl:with-param> in <xsl:apply-templates>, so you can do everything with this that you could do with a plain <xsl:call-template>.

    Also, the code above is somewhat lengthier than you might need because it tries to avoid using any XSLT extensions. If your processor supports exslt:node-set(), then you can just generate nodes directly using <xsl:element>, and use node-set() to convert the resulting tree fragment to a plain node to match against, without the need for document('') hack.

    For more information, see FXSL - it's a functional programming library for XSLT that is based on this concept.

    0 讨论(0)
提交回复
热议问题