在XSLT中,如何测试变量是否存在?

14

在使用XSLT时,如何测试本地范围变量是否存在,或者这是否可能?

6个回答

21
考虑将XSLT样式表视为XML DOM,变量声明元素使变量对所有后续兄弟和它们的后代可见。这允许XSLT处理器静态分析任何包含变量引用的XPath,以查看变量是否存在;如果变量声明存在于preceding-sibling或ancestor轴上,则变量引用是合法的,否则不合法。
请注意,这完全取决于XSLT的结构,而不是它正在处理的XML的结构。如果XPath表达式使用不存在的变量,XSLT处理器可以和应该产生错误。
无法在XSLT内部检查此条件,因为此条件在XSLT内部是不合法的。你在评论中描述的情况 - “想法是如果输出了某些内容,则设置标志变量,如果没有输出,则稍后显示不同的消息。” - 实际上应该导致语法错误。例如,如果您执行以下操作:
<xsl:if test="some_condition">
   <!-- produce output here -->
   <xsl:variable name="flag">true</xsl:variable>
</xsl:if>
<!-- time passes -->
<xsl:if test="$flag='true'>
   <!-- wouldn't it be nice? -->
</xsl:if>

如果您使用的是第二个xsl:if元素,而它既不是变量声明的后续同级元素,也不是其后代之一,则会出现语法错误。

这是我经常使用的一种技术 - 它可以根据各种不同的条件产生变量输出,而您不需要在以后重新检查它们:

<xsl:variable name="output">
   <xsl:if test="$condition1='true'">
      <p>condition1 is true</p>
   </xsl:if>
   <xsl:if test="$condition2='true'">
      <p>condition2 is true</p>
   </xsl:if>
   <xsl:if test="$condition3='true'">
      <p>condition3 is true</p>
   </xsl:if>
</xsl:variable>
<!-- we've produced the output, now let's actually *output* the output -->
<xsl:copy-of select="$output"/>
<!-- time passes -->
<xsl:if test="normalize-space($output) != ''">
   <p>This only gets emitted if $output got set to some non-empty value.</p>
</xsl:if>

更正式的解释为什么这是不必要的,因此也是不可能的,会得到+1。 - Tomalak
这似乎是一个不错的答案,可以将其置于顶部,因为它还提供了一种更好的方法来解决同样的问题。 - rjzii

12

询问这个问题表明您没有完全理解XSLT的关键点。:-)

XSLT是声明式的:除非您声明,否则任何东西都不存在。您声明一个变量,它就在那里;您不声明,它就不存在。

编码时不会有任何一点疑问,是否存在某个特定的变量。

XSLT具有严格的作用域规则,变量仅存在于其父元素的范围内(并非所有元素都能包含变量)。一旦离开父元素,该变量就消失了。

因此,除非您更明确地指定问题/意图,唯一有效的答案是该问题是错误的。您不需要在运行时检查变量是否存在。


这更多是XML设计不良的迹象,而不是其他什么。然而,我仍然很好奇是否有一些“技巧”可以让你做到这一点,类似于Muenchian方法有时被使用的方式。 - rjzii
这听起来有点像你从错误的角度去解决问题(过程式思维!)。我相信有一种方法可以实现你想要的。它是否优雅取决于你拥有什么(输入XML)以及你想要什么(输出格式)。 - Tomalak
1
@Tomalak - 实际上,我已经使用XSLT工作了几年,所以我认为这不是一个“从错误的方向看问题”的问题,而是我尝试处理的XML的原始格式只是错误的。因此,现在我们正在重写生成的XML,而编写XSLT变得非常简单。如果您在XSLT中执行某些操作似乎过于复杂,那么要么您做错了,要么XML结构错误,这里也存在代码异味。 - rjzii
1
它是声明性的:除非你声明它,否则什么都不存在。这取决于你所指的“你”。使用类似Symphony CMS的系统,可以为您生成参数以在模板中访问。但在某些情况下,只有在满足特定条件时才会生成参数。因此,实际上有些情况下,您可能需要在执行任何操作之前检查参数是否存在。 “提出这个问题表明您没有完全掌握XSLT的关键点。”或者也许您不知道某些实现? - Nathan Hornby
@Nathan 我明白你的意思,“你不知道的实现”是需要记在心里的。另一方面,XSLT 的主要用例是数据驱动/声明性(即模板匹配),而不是命令式(即 if/then/else),所以我认为只有当你做错了时,“这是否已经定义好了”这个问题才会出现。 - Tomalak
显示剩余9条评论

2

1
我认为这个答案可能是最有帮助的,但你可能没有得到任何投票,因为你只是贴了一个链接。也许加一点解释会更好? - Kyle Walsh
不是为了得票,但多解释一些是个好主意。这是一个关于谷歌的问题,所以我发布了来自谷歌的答案。 - StarSignLeo
@starsingleo - 有两个要点需要提出。 其中一个是该网站的常见问题解答未说明询问可在Google上找到答案的问题有任何问题。此外,如果您查看其他答案和一些讨论,会发现来自Google的结果所得出的解决方案最终成为特定于引擎,并且根据您的情况可能起作用也可能不起作用。因此,这意味着未来的其他开发人员也将看到那些解决方案可能有效也可能无效,并且绝对不是最佳实践。 这是仅通过搜索引擎搜索得不到的信息。 - rjzii
我只是说我把问题直接复制到谷歌上,它就是第一个响应。现在这篇帖子也是第一个。但如果你觉得需要因为我帮助你而打分,那是你的选择。 也许可以阅读一下关于打分的常见问题解答,“如果你发布了与主题无关或不正确的内容”。我相信那个链接是正确的? - StarSignLeo
@StarSignLeo - 我对你的回答给出了两个负面评价的原因。首先,你的回答只是复制粘贴了一个链接,对于刚接触这个问题的人来说,并没有真正解释任何内容,而其他回答则有更好的解释。然而,更重要的是,该链接仅说明了如何检查一个声明变量是否已经设置了值,而不是检查变量是否已经声明。正如其他回答所示,在XSLT中是不可能实现这一点的。如果你试图这样做,那么可能是你做错了什么,或者你正在处理的XML格式不正确。 - rjzii

1

检查值是否存在的最佳且快速的方法是检查其长度

<xsl:if test="string-length(value/to/check)=0">

</xsl:if>

0
如果你有一个变量,你可以通过以下方式来检查它是否有内容,或者它是否"存在":
<xsl:choose>
    <xsl:when test="$myvar">
        This variable exists!
    </xsl:when>
    <xsl:otherwise>
        The variable doesn't exist :(
    </xsl:otherwise>
</xsl:choose>

至于它的有效性,我不能确定。但是我可以告诉你,在我们工作中的一些系统中,我确实这样做了 ;)


如果“myvar”未设置,Firefox 3.5.2会返回一个错误。也许这与实现/XSLT版本有关?我似乎记得XSLT 2.0有一些很棒的函数,在许多XML引擎中不可用。 - rjzii
我不是完全确定。就个人而言,我认为不应该这样做。这是一个仅支持IE7的系统,也不确定浏览器是否会对其产生影响。 - Kieran Senior
2
那不是测试变量是否存在。那是测试上下文节点下是否存在名为“myvar”的元素。 - Robert Rossney

0
我认为这是不可能的,但你很可能永远不需要它,因为除非你声明了变量,否则它不存在。

事实证明,使用XSLT有点奇怪,因为有些东西不受支持(例如for循环),变量只能设置一次,不能更新。因此,其中一个想法是利用变量的存在作为标志,指示是否已运行转换的某些部分。理想情况下,您可以通过检查正在处理的XML中的某些内容的存在来实现这一点,但有时您没有这个选项。 - rjzii
1
@hacker: +1。基本上和我说的一样。@Rob: 我认为当你使用XSLT时,你必须放弃一堆“传统”的思维模式。您很可能也不需要检查转换的某些部分是否已运行,因为您可以(声明性地)通过检查它们是否会运行来找出。想知道是否创建了X部分?只需检查是否存在创建X部分的条件 - Tomalak
@Tomalak - 我知道这一点,而且我们甚至不得不寻找设置标志变量的方法,这表明生成的XML存在问题。 - rjzii
有时候,Tomalak最后一句话中的“just”是非常重要的。例如,一个可能会创建30个不同的<p>元素的转换,如果它产生任何内容,应该首先发出一个<h2>元素。如果您检查所有30个条件来确定是否发出<h2>,这不仅困难而且容易出错(并且维护起来很麻烦)。 (我帖子末尾的模式不是无缘无故出现的。) - Robert Rossney
一个参数可以由系统生成(比如Symphony CMS),所以有点多余的回答。这完全取决于具体的实现。 - Nathan Hornby
@NathanHornby,如果我们仔细阅读问题,就会注意到其中的“局部作用域变量”。 - Michael Krelin - hacker

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