XPath查找所有后续兄弟姐妹,直到特定类型的下一个兄弟

Phr*_*ogz 4 ruby xml xpath nokogiri

鉴于此XML/HTML:

<dl>
  <dt>Label1</dt><dd>Value1</dd>
  <dt>Label2</dt><dd>Value2</dd>
  <dt>Label3</dt><dd>Value3a</dd><dd>Value3b</dd>
  <dt>Label4</dt><dd>Value4</dd>
</dl>
Run Code Online (Sandbox Code Playgroud)

我想找到所有<dt>,然后,每个,找到以下<dd>直到下一个<dt>.

使用Ruby的Nokogiri我能够像这样完成:

dl.xpath('dt').each do |dt|
  ct  = dt.xpath('count(following-sibling::dt)')
  dds = dt.xpath("following-sibling::dd[count(following-sibling::dt)=#{ct}]")
  puts "#{dt.text}: #{dds.map(&:text).join(', ')}"
end
#=> Label1: Value1
#=> Label2: Value2
#=> Label3: Value3a, Value3b
#=> Label4: Value4
Run Code Online (Sandbox Code Playgroud)

但是,正如您所看到的,我在Ruby中创建一个变量,然后使用它编写一个XPath.我怎样才能编写一个相同的XPath表达式?

我猜到:

following-sibling::dd[count(following-sibling::dt)=count(self/following-sibling::dt)]
Run Code Online (Sandbox Code Playgroud)

但显然我不明白那self是什么意思.

这个问题与XPath类似:选择所有以下兄弟,直到另一个兄弟,除了'stop'节点没有唯一标识符.

这个问题与xpath几乎相同,除了我要求的XPath专用解决方案之外,找到所有以下兄弟相邻节点直到另一个类型.

jas*_*sso 5

这是个有趣的问题.大多数问题已经在@ lwburk的回答和评论中提到过了.为了向随机读者开放一些隐藏在这个问题中的复杂性,我的答案可能比OP需要的更复杂或更冗长.

XPath 1.0的功能与此问题有关

在XPath中,每个步骤以及所选节点集中的每个节点都独立工作.这意味着

  1. 子表达式没有通用方法来访问在先前子表达式中计算的数据,或者将在此子表达式中计算的数据共享到其他子表达式
  2. 节点没有通用方法来引用在先前子表达式中用作上下文节点的节点
  3. 节点没有通用的方法来引用当前选中的其他节点.
  4. 如果必须将所选节点的每个节点与同一个特定节点进行比较,那么该节点必须以所有选定节点共有的方式唯一定义

(嗯,实际上我并不是100%确定这个列表在每种情况下都是绝对正确的.如果有人对XPath的怪癖有更好的了解,请通过编辑来评论或更正这个答案.)

尽管缺乏通用解决方案,但如果对文档结构有适当的了解,则可以克服这些限制中的一些,和/或先前使用的轴可以与另一个用作反向链接的轴"恢复",即仅匹配使用的节点作为上一个表达式中的上下文节点.一个常见的例子parent是在首次使用child轴之后使用轴(相反的情况,从子到父,在没有附加信息的情况下不能唯一可恢复).在这种情况下,来自先前步骤的信息在稍后的步骤中更精确地重新创建(而不是访问先前已知的信息).

不幸的是,在这种情况下,除了使用XPath变量(需要事先定义)之外,我无法提出任何其他解决方案来引用以前已知的节点.

XPath指定了引用变量的语法,但没有指定定义变量的语法,如何定义变量的方式取决于使用XPath的环境.实际上,因为建议声明"用于评估子表达式的变量绑定始终与用于评估包含表达式的变量绑定相同",您还可以声称XPath明确禁止在XPath表达式中定义变量.

问题重新制定

在您的问题中,当给定a时<dt>,问题将是<dd>在切换上下文节点之后识别以下元素或最初给定的节点.识别最初给定的<dt>是至关重要的,因为对于要过滤的节点集中的每个节点,使用该节点作为上下文节点来评估谓词表达式; 因此<dt>,如果在上下文发生变化后无法识别它,则无法在谓词中引用原语.这同样适用于<dd>遵循给定兄弟姐妹的元素<dt>.

如果您正在使用变量,可以争论的是1)使用XPath变量语法和Nokogiri特定方式声明该变量或2)使用Nokogiri扩展XPath语法允许您在XPath表达式中使用Ruby变量之间存在重大差异.在这两种情况下,变量都是以特定于环境的方式定义的,只有当变量的定义也可用时,XPath的含义才会明确.使用XSLT可以看到类似的情况,在某些情况下,您可以选择1)<xsl:variable>在使用XPath表达式之前定义变量或2)使用current()(在XPath表达式中)XSLT扩展.

使用节点集变量和Kaysian方法的解决方案

您可以使用(设置A)选择当前<dd>元素后面的所有元素.你也可以选择所有的下列元素接下来将元素(集B).现在,设置差异会留下您实际想要的元素(元素集合在集合A中但不在集合B中).如果变量包含节点集A且变量包含节点集B,则可以通过(修改)Kaysian技术获得集合差异: <dt>following-sibling::dd<dd> <dt>following-sibling::dt[1]/following-sibling::ddA\B<dd>$setA$setB

dds = $setA[count(.|$setB) != count($setB)]
Run Code Online (Sandbox Code Playgroud)

没有任何变量的简单解决方法

目前,您的方法是选择所有<dt>元素,然后尝试<dd>在单个操作中将每个此类元素的值与相应元素的值耦合.是否可以转换该耦合逻辑以反过来工作?所以你先选择所有<dd>元素,然后为每个元素<dd>找到相应的元素<dt>.这意味着您最终会<dt>多次访问相同的元素,并且每次操作时只添加一个新<dd>值.这可能会影响性能,Ruby代码可能会更复杂.

好的一面是所需XPath的简单性.给定一个<dd>元素时,找到相应的元素<dt>非常简单:preceding-sibling::dt[1]

适用于您当前的Ruby代码

dl.xpath('dd').each do |dd|
  dt = dd.xpath("preceding-sibling::dt[1]")
  ## Insert new Ruby magic here ##
end
Run Code Online (Sandbox Code Playgroud)