xsl: 如何在“match”内部使用参数?

6

我的xsl文件有一个参数

<xsl:param name="halfPath" select="'halfPath'"/>

我想在match内部使用它

<xsl:template match="Element[@at1='value1' and not(@at2='{$halfPath}/another/half/of/the/path')]"/>

但这并不起作用。我猜想我不能在''内使用参数。如何修复/解决这个问题?
4个回答

7

XSLT 1.0 W3C规范禁止在匹配模式中引用变量/参数,请参考此处

"match属性的值包含VariableReference是错误的"

XSLT 2.0没有这种限制,因此使用XSLT 2.0

如果无法使用XSLT2.0,则将指令的完整内容放在中,其中测试与匹配模式结合使用等效于包含变量/参数引用的XSLT 2.0匹配模式。

在更复杂的情况下,如果有多个模板匹配相同类型的节点,但具有引用变量/参数的不同谓词,则需要使用包装的而不是包装的。


抱歉这段注释很混乱,我会将其删除。同时在我的问题中,我准备了一个干净的示例。 - Jagger

6

那么,您可以在模板中使用条件指令:

<xsl:template match="Element[@at1='value1']">
  <xsl:if test="not(@at2=concat($halfPath,'/another/half/of/the/path'))">
    .. do something
  </xsl:if>
</xsl:template>

您只需要知道这个模板会处理所有满足第一个条件的元素。如果您有另一个模板可以处理与第一个条件匹配但不符合第二个条件的元素,则使用 <xsl:choose>,并将另一个模板的内容放在 <xsl:otherwise> 块中。

或者,如果您可以切换到 XSLT2 处理器,则 XSLT2 可以直接处理它。


也许由于某种原因,我必须在我的参数上使用 string(),否则它无法正常工作... string($halfPath) - Oleg Vazhnev
你可以在模板中使用条件语句,但上面显示的代码是不正确的。一个“测试”表达式不是属性值模板,所以大括号{}不会替换。相反,你正在测试@at2是否等于字面字符串“{$halfPath}/...”,这不是你想要的。请参见@MichaelKay的答案获取更多细节。 - LarsH
1
@LarsH:说得好 - 我已经从问题中逐字复制了条件,我没有注意到它无效。现在已经修复了。 - Flynn1179
观察:当使用标识模板将非常大的XML文件复制到整个文件中仅出现了很少的属性实例进行修改时,至少在一个XSLT 1.0处理器中,在模板内部使用xsl:if或xsl:choose比使用匹配表达式慢几个数量级(使用硬编码文本来解决无法在匹配表达式中使用变量/参数的问题)。这说明什么?无法在匹配表达式中使用变量的代价可能会极大地影响是否值得忍受XSLT 2.0升级困难以实现自己的目标。 - kbulgrien

2

这个话题回答了我的问题,但是Flynn1179提出的解决方案对我来说不太正确(因人而异)。所以尝试按照比我更专业的人建议的方式进行操作,但如果它对你没有用,请考虑我是如何解决它的。我正在使用只处理XSL版本1.0的xsltproc。

我需要匹配<leadTime小时="0024">,但要使用参数:<xsl:param name="hour">0024</xsl:param>。我发现: <xsl:if test="@hour='{$hour}'">没有起作用,尽管这里和其他地方的声明是XSL v.1.0所需的语法。 相反,更简单的<xsl:if test="@hour=$hour">就可以胜任工作。

还有一点:Dimitre在上面建议将模板放在if语句中。xsltproc对此抱怨:相反,我将if语句放在模板内部:

<xsl:template match="leadTime">
  <xsl:if test="@hour=$leadhour">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:if>
</xsl:template>

1
注意到被接受的答案代码有误,并提供了可行的解决方案,值得点赞。顺便说一下,Dimitre 的意思不是将模板放在 if 语句中,而是将模板的主体(内容)放在 if 语句中。 - LarsH

1
在XSLT 2.0中,您可以在匹配模式内引用全局变量,但语法比您猜测的要简单。
<xsl:template match="Element[@at1='value1' and 
              not(@at2=$halfPath/another/half/of/the/path)]"/>

而不是

<xsl:template match="Element[@at1='value1' and 
              not(@at2='{$halfPath}/another/half/of/the/path')]"/>

此外,语义并不是您所期望的那样:在“/”的左侧引用的变量必须包含一个节点集,而不是XPath表达式的片段。

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