XPath选择节点直到条件满足

3

我有一个类似于以下内容的HTML/XML文档。在任意重复的模式下,同一颜色可以有一个或多个相同的'tr',然后切换到另一种颜色。这是一个例子:

<tr class='red'></tr>
<tr class='blue'></tr>
<tr class='red'></tr>
<tr class='red'></tr>
<tr class='red'></tr>
<tr class='blue'></tr>
<tr class='blue'></tr>
<tr class='red'></tr>
<tr class='red'></tr>
<tr class='blue'></tr>

我需要的是一个XPath(1.0)表达式。从任何颜色“块”的第一个“tr”开始(请注意,没有标记指示这些块,仅有颜色的变化),仅选择该块内部的后续“tr”。
我尝试了以下表达式:
./following-sibling::tr[@class=preceding-sibling::tr[1]/@class]

但是这也会选择后续块中第二个及以上的'tr'。我感觉我已经接近所需,但还不能完全实现。

提前感谢。

编辑:期望输出是一个节点集,其中包含块内后续的'tr'(只有该块)。


我有点困惑...你能发一下期望的输出吗? - Lukas Eder
例如,如果我的起始点是第三个'tr'(红色),我只会选择第四和第五个'tr'。 - user1300244
2个回答

3

此XPath 1.0表达式选择第一个蓝色 tr 元素的“块”:

      (/*/tr[@class='blue'][1] | /*/tr[@class='blue'][1]/following-sibling::tr)
        [count(. | /*/tr[@class='blue'][1]
                          /following-sibling::tr
                                    [not(@class='blue')][1]
                                       /preceding-sibling::*
               )
        =
         count(/*/tr[@class='blue'][1]
                          /following-sibling::tr
                                    [not(@class='blue')][1]
                                       /preceding-sibling::*
         )
         ]

说明:

使用众所周知的Kayessian公式来进行节点集交集计算:

$ns1[count(.|$ns2) = count($ns2)]

这个XPath表达式选择属于节点集$ns1和节点集$ns2的节点。
在这种情况下,我们只需用适当的具体XPath表达式替换$ns1$ns2 -- 一个是第一个蓝色tr及其后续兄弟节点,另一个是第一个蓝色tr之后的第一个非蓝色tr及其前面的所有兄弟节点。这两个节点集的交集正是所需的第一个蓝色tr块。 XSLT - 基于验证:
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
  <xsl:copy-of select=
  "(/*/tr[@class='blue'][1] | /*/tr[@class='blue'][1]/following-sibling::tr)
            [count(. | /*/tr[@class='blue'][1]
                              /following-sibling::tr
                                        [not(@class='blue')][1]
                                           /preceding-sibling::*
                   )
            =
             count(/*/tr[@class='blue'][1]
                              /following-sibling::tr
                                        [not(@class='blue')][1]
                                           /preceding-sibling::*
                 )
             ]
  "/>
 </xsl:template>
</xsl:stylesheet>

当将此转换应用于以下 XML 文档时:
<t>
    <tr class='red'></tr>
    <tr class='red'></tr>
    <tr class='red'></tr>
    <tr class='red'></tr>
    <tr class='blue'></tr>
    <tr class='blue'></tr>
    <tr class='red'></tr>
    <tr class='red'></tr>
    <tr class='blue'></tr>
</t>

XPath表达式会被评估并将所选节点复制到输出中:
<tr class="blue"/>
<tr class="blue"/>

谢谢您的回答。然而,我正在寻找一般的XPath,以选择使用其第一个<tr>作为起点的任何块。我理解集合交集的概念,并尝试自己重写表达式,但无法使其正常工作。如果您有时间,我将非常感激提供一般解决方案 :) - user1300244
抱歉,我不同意。问题陈述为:“我要找的是一个 XPath(1.0)表达式,它从任何颜色 'block' 中的第一个 'tr' 开始,仅选择该块内随后的 'tr'。” - user1300244
@user1300244:这根本没有明确定义。 "任何块"的意思是什么?如果您想要XPath表达式选择所需的节点,则需要确切地指定此内容。例如,我的答案中的表达式显示了如何选择第一个蓝色块的所需节点。您需要说哪个块--如果不使用变量引用,则通常无法指定"任何块"。 - Dimitre Novatchev
啊,那正是我所希望做的。谢谢您花费时间,即使它并不完全回答了我的问题,我还是学到了一些东西。 - user1300244
1
太棒了。佩服。省了我很多麻烦。 - TheRajVJain
显示剩余2条评论

0
如果您有一个绑定到起始节点的变量$v$,那么我认为可以像这样完成(效率极低):
$v/following-sibling::tr[@class = $v/@class and count(preceding-sibling::tr[not(@class=$v/@class)] = count($v/preceding-sibling::tr[not(@class=$v/@class)])]

如果你的 API 不提供绑定变量的机会,那么我认为这是做不到的,虽然我很乐意被证明是错误的。
你没有说明你的限制是什么,但 XPath 1.0 对于这个特定的问题似乎不是一个好的技术选择。
即使在 XPath 2.0 中,它也不是特别好。你真正需要递归,并且这意味着使用 XQuery 或 XSLT 而不是纯粹的 XPath。

很遗憾,我无法绑定变量。限制条件是我只能使用XPath 1.0,但由于数据集很小,效率不是问题。还是谢谢你抽出时间来回答 :) - user1300244

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