在apply-templates模式中使用变量的值

15

我想应用一个模板,其模式取决于一个变量的值。

<xsl:variable name="mode" select="@attribute"/>
<xsl:apply-templates mode="{$mode}"/>

我遇到了样式表无法编译的错误。mode的值应该是一个QName,但它是“{$mode}”。

是否有可能根据变量使用不同的模式?


你想做的语法上是不合法的,但在XSLT 1.0中可以使用“模板引用”原则来实现相同的效果,这也是FXSL库的基础。 - Dimitre Novatchev
谢谢你提供这个想法!我尝试理解它,但我在XSLT方面还很新,并且对函数式编程没有太多经验。对于所描述的情况,这是“过度工程化”的。也许,当我更有经验时,我可以有时候使用它。 - maria90
maria90,知道这种技术的存在就足够了——需要时随时使用它。 - Dimitre Novatchev
3个回答

9
您唯一能够根据表达式使用特定模式的选项是使用
<xsl:choose>
   <xsl:when test="@attribute = 'foo'">
      <xsl:apply-templates mode="bar"/>
   </xsl:when>
   <xsl:otherwise>
      <xsl:apply-templates/>
   </xsl:otherwise>
</xsl:choose>

或者使用 xsl:if 标签实现相同的功能。在 XSLT 1.0 中,mode 属性值必须是一个 QName,而在 XSLT 2.0 中允许是一个 QName 或特殊标记,如 #current#default'。但你不能在运行时计算 mode 的值。


这是我解决问题的第一个想法。我想避免测试变量的特定值。尽管如此,我还是会采用这种方式,因为FXSL对我来说有点沉重。 - maria90

5

mode 不是属性值模板(AVT)的有效候选项。你不能这样做。

根据 XSLT 2.0 spec:

[定义:在被指定为属性值模板的属性中,例如文字结果元素的属性,可以使用表达式,方法是用大括号({})将表达式括起来]。

mode 在规范中没有被指定为 AVT,因此你不能这样做。


2
我收到了一个错误,样式表无法编译。mode的值应该是一个QName,但它是“{$mode}”。是否有可能使用与变量相关的模式?
不,这在任何XSLT版本(1.0、2.0或3.0)中都不支持。
由于您实际上正在尝试模拟高阶函数(HOF),因此可以使用FXSL的基本原理在XSLT 1.0中完成此操作。
FXSL 1.x是一个纯XSLT 1.0编写的模板库,支持/模拟HOF。
以下是基于这些原则的完整解决方案:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://fxsl.sf.net" exclude-result-prefixes="f">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

   <f:inc/>
   <f:double/>

 <xsl:variable name="vModeInc" select="document('')/*/f:inc[1]"/>
 <xsl:variable name="vModeDouble" select="document('')/*/f:double[1]"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <nums>
    <xsl:apply-templates select="$vModeInc">
      <xsl:with-param name="pNodes" select="node()"/>
    </xsl:apply-templates>
  </nums>
==============
  <nums>
    <xsl:apply-templates select="$vModeDouble">
      <xsl:with-param name="pNodes" select="node()"/>
    </xsl:apply-templates>
  </nums>
 </xsl:template>

 <xsl:template match="f:inc">
   <xsl:param name="pNodes"/>
   <xsl:apply-templates select="$pNodes" mode="incr"/>
 </xsl:template>

 <xsl:template match="f:double">
   <xsl:param name="pNodes"/>
   <xsl:apply-templates select="$pNodes" mode="double"/>
 </xsl:template>

 <xsl:template match="num" mode="incr">
  <num><xsl:value-of select=".+1"/></num>
 </xsl:template>

 <xsl:template match="num" mode="double">
  <num><xsl:value-of select=".*2"/></num>
 </xsl:template>
</xsl:stylesheet>

当对下面的XML文档应用此转换时:
<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

期望的、正确的结果已经生成——在可用的两种模式之一中处理了 nums/num 元素,具体取决于指定的变量:$vModeInc(每个值增加1)或 $vModeDouble(每个值乘以2)。
<nums>
   <num>2</num>
   <num>3</num>
   <num>4</num>
   <num>5</num>
   <num>6</num>
   <num>7</num>
   <num>8</num>
   <num>9</num>
   <num>10</num>
   <num>11</num>
</nums>
==============
  <nums>
   <num>2</num>
   <num>4</num>
   <num>6</num>
   <num>8</num>
   <num>10</num>
   <num>12</num>
   <num>14</num>
   <num>16</num>
   <num>18</num>
   <num>20</num>
</nums>

+1 这真聪明。我没有跟上 FXSL 的最新动态,但一直希望有时间去了解它。 :-) 对于任何其他正在阅读的人来说,跟踪上面的 XSLT 并查看它在做什么是值得花时间的。 - LarsH
@LarsH,谢谢,刚刚阅读了关于FXSL 2的《极端标记语言》论文,不需要太多时间。 :) - Dimitre Novatchev
你能给我那篇论文的链接吗?我谷歌搜索到了这个网站:http://fxsl.sourceforge.net/,里面有两篇论文的链接,但都已经失效了。哦,是这个吗?http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html - LarsH
@LarsH:这个没有问题(来自我的stackoverflow档案):http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html - Dimitre Novatchev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接