如何根据变量调用命名模板?

发布于 2024-07-30 09:28:20 字数 640 浏览 9 评论 0原文

我不知道是否可能,但我想知道如何做到这一点...

假设我们有以下 XSL:

<xsl:template name="foo">
  Bla bla bla
</xsl:template>
...
<xsl:template name="bar">
  Bla bla bla
</xsl:template>
...
<xsl:template match="/">
  <xsl:if test="$templateName='foo'">
    <xsl:call-template name="foo"/>
  </xsl:if>
  <xsl:if test="$templateName='bar'">
    <xsl:call-template name="bar"/>
  </xsl:if>
</xsl:template>

是否可以更改 XSL 以读取类似...

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

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

Let's say we have the following XSL:

<xsl:template name="foo">
  Bla bla bla
</xsl:template>
...
<xsl:template name="bar">
  Bla bla bla
</xsl:template>
...
<xsl:template match="/">
  <xsl:if test="$templateName='foo'">
    <xsl:call-template name="foo"/>
  </xsl:if>
  <xsl:if test="$templateName='bar'">
    <xsl:call-template name="bar"/>
  </xsl:if>
</xsl:template>

Is it possible to change the XSL to read something like...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

怎言笑 2024-08-06 09:28:20

这不可能完全像您所描述的那样,但是如果您希望能够在运行时根据您在其他地方设置的某个值来选择模板,那么有一个技巧可以做到这一点。 这个想法是让您的命名模板也以不同的模式匹配具有相应名称的节点(这样它就不会扰乱您的正常转换),然后进行匹配。 例如:

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

请注意,您可以在 中使用 ,这样您就可以用它做所有您可以做的事情使用普通的 即可。

另外,上面的代码比您可能需要的要长一些,因为它试图避免使用任何 XSLT 扩展。 如果您的处理器支持 exslt:node-set(),那么您可以直接使用 生成节点,并使用 node-set( ) 将生成的树片段转换为普通节点以进行匹配,而不需要 document('') hack。

有关详细信息,请参阅 FXSL - 它是 XSLT 的函数式编程库,就是基于这个概念。

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.

挽袖吟 2024-08-06 09:28:20

不,这是不可能不是直接可能的。 调用约定是:

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

其中 QName 定义为

QName ::= PrefixedName | UnprefixedName

PrefixedName   ::= Prefix ':' LocalPart
UnprefixedName ::= LocalPart

Prefix         ::= NCName
LocalPart      ::= NCName

基本上这可以归结为“只有字符,没有表情”。 正如其他答案所强调的那样,实际上有一些方法可以做等效的事情,但直接的方法/天真的方法行不通。

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.

紅太極 2024-08-06 09:28:20

供任何人将来参考:

这是一个基于 Pavel Minaev 答案的工作示例。 我没有原创的想法。 ;-) 我将其切换为使用 msxml:node-set 正如他所描述的(或多或少),以便它可以在 .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>

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>
吝吻 2024-08-06 09:28:20

更新:下面的链接已更新为指向 web.archive.org - 不幸的是,IDEALLIANCE 已使所有Exteme Markup Languages会议记录不可用...
在适当的时候,我会为这两篇文章找到一个更永久的位置。


这是在 FXSL 中实现的。

对 FXSL 的主要原理有很好的解释。

请参阅以下两篇文章:

使用 FXSL 库在 XSLT 中进行函数式编程”(适用于 XSLT 1.0),(PDF),网址为:

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

(HTML)位于:

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

使用 XSLT 2.0 和 FXSL 进行高阶函数编程”(PDF),位于:

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

(HTML)位于:
http://conferences.idealliance.org/extreme/html/2006/ Novaatchev01/EML2006Novatchev01.html

使用 FXSL,我已经能够轻松、优雅地解决许多“对于 XSLT 来说不可能”的问题。 此处可以找到很多示例。

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.

情未る 2024-08-06 09:28:20

我想我或多或少有和你一样的问题。 我有一个“外部”模板,并且想根据运行时设置的某些变量来调用不同的“内部”模板。 我通过谷歌搜索找到了您的问题,以获取动态 的方法。 我通过使用 解决了这个问题,如下所示。

输入 XML(在运行时生成)包含以下内容:

<template name="template_name_1"/>

“外部”模板中的 XSL 具有:

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

(此 apply-templates 中的 select="template" 指的是到输入 XML 中的

最后是“内部”模板,我希望将其作为 name 值的结果包含在内我的 XML 中的 code> 属性看起来像:

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

(同样,match="template[@name='xyz']" 引用了之前的 select="template"< /code> 并依次到输入 XML 中的

通过这种方式,我可以动态地“调用“模板只需通过我的输入 XML 进行控制。

这可能与您试图解决的问题不同,但我认为它非常接近,并且比本页其他地方提到的 FSXL 解决方案简单得多。

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.

温暖的光 2024-08-06 09:28:20

这个怎么样?:

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

您可以使用变量来简化模板的“调用”,与之前的贡献中所述类似:

<xsl:variable name="templates" select="document('')/*/xsl:template"/> 

<xsl:apply-templates select="$templates[@name=$templateName]"/>
<xsl:apply-templates select="$templates[@name='foo']"/>

请注意,可以照常使用可选的

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.

℉絮湮 2024-08-06 09:28:20

谢谢@Pavel、@Dimitre 的出色回答。 这是另一个工作示例,基本上回收了 @Pavel 的方法以供将来参考......

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

  <xsl:variable name="templates">
    <xsl:for-each select="('foo','bar')">
      <xsl:element name="{.}"/>
    </xsl:for-each>
  </xsl:variable>
  
  <xsl:template name="foo" match="foo" mode="call-template">
    Foo foo foo
  </xsl:template>
  
  <xsl:template name="bar" match="bar" mode="call-template">
    Bar bar bar
  </xsl:template>
  
  <xsl:template match="/">
  
    <xsl:for-each select="('bar','foo','foo','bar')">
      <xsl:variable name="template-name" select="." />
      <xsl:apply-templates select="$templates/*[local-name() = $template-name]"  mode="call-template"/>
    </xsl:for-each>
    
    
  </xsl:template>

</xsl:stylesheet>

Thank you @Pavel, @Dimitre for the excellent answers. Here is another working example that basically recycles @Pavel's approach for future reference...

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

  <xsl:variable name="templates">
    <xsl:for-each select="('foo','bar')">
      <xsl:element name="{.}"/>
    </xsl:for-each>
  </xsl:variable>
  
  <xsl:template name="foo" match="foo" mode="call-template">
    Foo foo foo
  </xsl:template>
  
  <xsl:template name="bar" match="bar" mode="call-template">
    Bar bar bar
  </xsl:template>
  
  <xsl:template match="/">
  
    <xsl:for-each select="('bar','foo','foo','bar')">
      <xsl:variable name="template-name" select="." />
      <xsl:apply-templates select="$templates/*[local-name() = $template-name]"  mode="call-template"/>
    </xsl:for-each>
    
    
  </xsl:template>

</xsl:stylesheet>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文