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

时间:2009-08-05 14:41:28

标签: xslt fxsl

我不知道是否可能,但我想知道该怎么做......

假设我们有以下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>

6 个答案:

答案 0 :(得分:12)

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

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

请注意,您可以在<xsl:with-param>中使用<xsl:apply-templates>,因此您可以使用普通<xsl:call-template>执行此操作。

此外,上面的代码比您可能需要的更长,因为它试图避免使用任何XSLT扩展。如果您的处理器支持exslt:node-set(),那么您可以直接使用<xsl:element>生成节点,并使用node-set()将生成的树片段转换为普通节点以进行匹配,而无需{ {1}}黑客。

有关更多信息,请参阅FXSL - 它是基于此概念的XSLT函数式编程库。

答案 1 :(得分:6)

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

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

QName is defined as

的位置
QName ::= PrefixedName | UnprefixedName

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

Prefix         ::= NCName
LocalPart      ::= NCName

基本上归结为“只有字符,没有表达”。正如其他答案所强调的那样, 实际上是做同等事情的方法,但直截了当的方法/天真的方法是行不通的。

答案 2 :(得分:3)

任何人未来的参考:

这是一个基于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>

答案 3 :(得分:2)

更新:以下链接已更新为指向web.archive.org - 遗憾的是,IDEALLIANCE已将所有<​​em> Exteme Markup Languages 会议程序设为无法使用... 在适当的时候,我会为这两篇文章找到一个更永久的地方。


这是在FXSL

中实施的

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

请参阅以下两篇文章:

使用FXSL库的XSLT中的函数编程”(对于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



使用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)at: http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html



使用FXSL我已经能够轻松优雅地解决许多问题,这些问题似乎“对XSLT来说是不可能的”。人们可以找到很多例子here

答案 4 :(得分:2)

我认为我和你的问题差不多。我有一个“外部”模板,并希望根据运行时设置的某个变量调用不同的“内部”模板。我通过Google搜索找到了一个让您拥有动态<xsl:call-template>的方法的问题。我使用<xsl:apply-templates>来解决它,如下所示。

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

<template name="template_name_1"/>

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

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

(此apply-templates中的select="template"引用输入XML中的<template>标记

最后,由于我的XML中name属性的值,我希望包含的“内部”模板如下所示:

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

(同样,match="template[@name='xyz']"引用之前的select="template",然后引用输入XML中的<template>标记及其name属性

通过这种方式,我可以简单地通过输入XML控制动态“调用”模板。

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

答案 5 :(得分:0)

这个怎么样?:

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

您可以简化&#34;电话&#34;使用类似于早期贡献中描述的变量的模板:

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

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

请注意,可以照常使用可选的<xsl:with-param>