XPath (1.0) 匹配连续元素直到特定的子元素或结束

XPath (1.0) Match consecutive elements until specific child or end

这是针对 XPath 1.0 的。

这是我匹配的标记示例。元素的实际数量不是提前知道的,因此会有所不同,但遵循这种模式:

<div class="entry">
    <p><iframe /></p>
    <p>Text 1</p>
    <p>Text 2</p>
    <p>Test 3</p>
    <p><iframe /></p>
    <p>
        <a>Test 4</a>
        <br />
        <a>Test 5</a>
    </p>
</div>

我正在尝试匹配 而不是 的每个 <p> 包含 <iframe>,直到 的下一个 <p> 包含一个 <iframe> 或直到封闭的 <div> 元素结束。

为了让事情稍微复杂一些,出于特定原因我需要使用每个 <iframe> 作为基础,a la //div[@class='entry']//iframe,这样每个节点集都基于

(//div[@class='entry']//iframe)[1]
(//div[@class='entry']//iframe)[2]
...

因此,在这种情况下,匹配

<p>Text 1</p>
<p>Text 2</p>
<p>Test 3</p>

<p>
    <a>Test 4</a>
    <br />
    <a>Test 5</a>
</p>

分别

我尝试了以下一些测试无济于事:

(//div[@class='entry']//iframe)/ancestor::p/following-sibling::p[preceding-sibling::p[iframe]]

(或用于测试):

(//div[@class='entry']//iframe)[1]/ancestor::p/following-sibling::p[preceding-sibling::p[iframe]]
(//div[@class='entry']//iframe)[2]/ancestor::p/following-sibling::p[preceding-sibling::p[iframe]]

及其一些变体,但第一组发生的事情是它获取所有 <iframe>-less <p> 元素一直到最后,而不是在下一个 <p> 处停止包含 <iframe>.

我已经有一段时间了,尽管我通常对这类事情很得心应手,但我无法完全按照自己的方式工作,[=54 的搜索结果都没有=] 等等有帮助。

谢谢。任何帮助总是感激。

编辑:可以假定 <div class="entry"> 在文档中只出现一次。

我不确定我是否完全理解,但有时对尝试的解决方案发表评论比尝试解释更有帮助。

请尝试以下 XPath 表达式:

//div[@class='entry']//iframe//p[not(descendant::iframe)]

如果结果正确,请告诉我。

如果没有,

  • 解释结果与您需要的有何不同
  • 请展示一个更完整的 HTML 示例:一个包含多个 div 元素的合理文档,以及多个 div[@class = 'entry'] 元素 - 否则涵盖您描述的所有复杂性。
  • 解释为什么要在表达式中添加 [1][2]
  • 提供有关您使用 XPath 的平台的更多详细信息,也许 post 代码

如果没有帮助,您无法用一个 XPath 1.0 表达式完成您的要求。问题是你想问的问题是

Starting from an element X (the p-containing-an-iframe), find the other p elements for which that element's nearest preceding p-with-an-iframe is the original node X

如果我们有一个变量 $x 保存对顶级上下文节点(我们开始的 p[iframe])的引用,那么您可以说类似下面的内容(在 XPath 2.0 中) )

following-sibling::p[not(iframe)][preceding-sibling::p[iframe][1] is $x]

XPath 1.0 没有 is 运算符来比较节点身份,但您可以使用其他代理,例如

following-sibling::p[not(iframe)][count(preceding-sibling::p[iframe])
                               = (count($x/preceding-sibling::p[iframe]) + 1)]

p 之后比 $xpreceding-sibling::p[iframe] 的元素。

那么问题的核心是如何从内部谓词内部获取外部上下文节点——纯 XPath 1.0 无法做到这一点。在 XSLT 中,您有 current() 函数,但除此之外,您有两个基本选择:

  • 如果您的 XPath 库允许您为表达式提供变量绑定,则注入一个包含上下文节点的变量 $x 并使用我在上面给出的表达式。
  • 如果您不能注入变量,则按顺序使用两个单独的 XPath 查询。

首先执行表达式

count(preceding-sibling::p[iframe]) + 1

与相关的p[iframe]作为上下文节点,并将结果作为数字。或者,如果您已经在宿主语言中迭代这些 p[iframe] 元素,那么只需直接从那里获取迭代编号,您不需要使用 XPath 对其进行计数。无论哪种方式,您都可以动态构建第二个表达式:

following-sibling::p[not(iframe)][count(preceding-sibling::p[iframe]) = N]

(其中 N 是第一个 expression/iteration 计数器的结果)并使用相同的上下文节点对其进行评估,将最终结果作为节点集。