我经常遇到想要选择前一个“表兄弟”到节点的问题。
例如:
<root>
<level1>
<level2 id="1"/>
<level2 id="2"/>
<level2 id="3"/>
<level2 id="4"/>
</level1>
<level1>
<level2 id="5">
<level2 id="9"/>
</level2>
<level2 id="6"/>
<level2 id="7"/>
<level2 id="8"/>
</level1>
</root>
如果上下文节点是
/root/level1/level2[@id='6']
我不想"id=9"
;我想"id=5"
。
类似地,如果上下文节点是
/root/level1/level2[@id='5']
我想"id=4"
。
所以......preceding-sibling
在前一种情况下有效,但在后者中无效。 preceding
在后一种情况下选择,但在前一种情况下不选择。(可能还有很多其他情况。)
所以我当前的解决方案是这样的(用作id=6
上下文节点...我可以将它放在一个函数中,但这应该足够清楚)
(/root/level1/level2[@id='6']/preceding::level2 intersect /root/level1/level2)[last()]
这确实有效,但似乎......复杂,并且可能效率低下......我是否缺少一个更简单的解决方案?
(我目前在 XSLT 3 中使用 XPath 3.1,但我认为这是一个普遍问题。)
(我已将其限制为 XPath 2.0+,但我可能会向 XPath 1.0 提出问题,因为这看起来更糟糕,因为它intersect
不存在......但我将在另一篇文章中这样做。)
对于 XSLT 1.0,这是有效的:
/root/level1/level2[@id='6']/preceding::level2[parent::level1[parent::root]][1]
并且可以说是一个更好的解决方案。
我想做的是
/root/level1/level2[@id='6']/preceding::(root/level/level2)[1]
并将其解释为路径上的匹配root/level/level2
,但我认为这是无效的?
这里有一些非常好的答案,我想完全弄清楚问题是什么,抱歉。
我正在寻找所有“第 n 个表兄弟”,其中第 0 个表兄弟是兄弟姐妹。
令人恼火的是,优雅的解决方案只是获得第 0 和第 1,而更复杂的解决方案是“正确的”,但相当笨重(无意冒犯)。
目前,我拥有的最优雅的解决方案是(作为程序员)采用绝对简单的路径到达当前节点,在本例中
let $this = ....
(root/level1/level2)[. << $this][last()]
聪明的解决方案以编程方式执行此操作(一个使用深度,另一个使用路径),并且对我来说有点令人难以置信。
优雅的解决方案是使用“<<”运算符,但仅匹配第 0 个和第 1 个兄弟节点(这是一个合理的解释)。
XSLT 1.0 解决方案同样只执行第 0 个和第 1 个,我想我实际上会使用parent::
上面的解决方案,并将完整路径匹配到根以确保所有表兄弟都匹配。
(当然,这假设您知道完整路径,尽管 martin honnens 解决方案通常有效)。
如果我正确理解您的需求,XPath 1.0 就是您所需要的。
给定您的输入 XML,此XSLT 2.0(对于测试程序 - 关键 XPath 是 1.0),
产生这些测试结果,
它涵盖了两种令人关注的情况,并为所有起始
level2
元素产生了合理的结果。我想不出一个简单的表达式,我想知道以下代码是否使用
path
并xsl:evaluate
实现了要求:path
使用该函数,然后进行字符串替换,看起来有点像“黑客” ,xsl:evaluate
但我没有看到简单、直接的 XPath。我想我可能会做
或 XSLT 中的等效项。
XPath 1.0 解决方案是
为了实现 Nth 兄弟,其中 N 是动态提供的参数,我会编写一个递归函数,这必然使其成为 XPath 2.0+。
在 XQuery 表示法中