层次结构中带有可选元素的 XPath

XPath with optional element in hierarchy

提问人:Phrogz 提问时间:1/6/2011 最后编辑:CommunityPhrogz 更新时间:9/3/2016 访问量:15877

问:

就像这个 Stack Overflow 答案一样,假设您需要选择一个特定的表,然后选择它的所有行。由于 HTML 的宽松性,以下三个都是合法标记:

<table id="foo"><tr>...</tr></table>
<table id="foo"><tbody><tr>...</tr></tbody></table>
<table id="foo"><tr>...</tr><tbody><tr>...</tr></tbody></table>

您担心表嵌套在表中,因此不想使用像 .
table[@id="foo"]//tr

如果可以将所需的 XPath 指定为正则表达式,它可能如下所示:
table[@id="foo"](/tbody)?/tr

通常,如何指定允许选择器层次结构中可选元素的 XPath 表达式?

需要明确的是,我不是要解决现实世界的问题或选择特定文档的特定元素。我要求解决一类问题的技巧。

HTML XPath

评论

0赞 Phrogz 1/6/2011
哦,为了简单和通用,我忽略了 HTML 中合法的 and 元素。theadtfoot

答:

8赞 Dimitre Novatchev 1/6/2011 #1

用途

   //table[@id="foo"]/*[self::tbody or self::thead or self::tfoot]/tr
   |
   //table[@id="foo"]/tr

选择作为具有属性“foo”的 any 的子元素的任何元素,或选择作为子 any 的子元素的任何元素。trtableidtrtbodytable

评论

0赞 Phrogz 1/6/2011
我很欣赏你在这方面的专业知识,但这真的是可以做到的最好的吗?如果 xpath 的第一部分和最后一部分只是“table”和“tr”,这还不错,但是对于类似的东西,围绕一个变体复制表达式变得非常不干燥div[@id="contents]//table[@class="comments"](/tbody)?/tr/[td//text()[contains(., 'targetString')]]
0赞 Dimitre Novatchev 1/6/2011
@Phrogz:不,这几乎和我最初的表达一样简单——见编辑。使用 XPath 2.0 时,它可以更加优雅,而对于具有已知 XML 模式的 XML 文档(XHTML 就是这种情况),它甚至会更加优雅。
0赞 Steven D. Majewski 1/30/2013
XPath 2.0 版本是什么?我能想到的最好的是交替使用“.”和可选部分的步骤。使用 TEI 上的 Saxon,这对我有用:/TEI.2/text/(.|组/文本)/正文/div1
1赞 Dimitre Novatchev 1/30/2013
@StevenD.Majewski,//table[@id="foo"]/(tr|(tbody|thead|tfoot)/tr)
25赞 user357812 1/6/2011 #2

我不明白为什么你不能使用它:

//table[@id='foo']/tr|//table[@id='foo']/tbody/tr

如果您想要一个没有节点集联合的表达式:

//tr[(.|parent::tbody)[1]/parent::table[@id='foo']]

评论

0赞 Phrogz 1/7/2011
你的第一个答案是迪米特的建议。但是,我已经改变了对您的第二个表达方式的接受,因为这更干燥。
1赞 Dimitre Novatchev 1/7/2011
@Phrogz:该表达式包含后向轴,并且比仅包含正向轴的表达式效率低。至于“DRY”性,你可能还需要考虑一个表达式的可理解性,这当然与它的可维护性是相互关联的。:)
0赞 Phrogz 1/7/2011
@Dimitre 感谢您对效率的评论。你是对的,简单的交替(你的答案和这个答案中的第一个表达)更容易理解,即使它更容易出现编辑错误并且更难维护。
1赞 Dimitre Novatchev 1/7/2011
@Phrogz:更易于理解意味着更易于维护!
1赞 1/7/2011
@Phrogz:我还认为联合表达式更简单易读。但是,除非你有一个非常切肉刀的 XPath 引擎,否则我认为联合表达式(带有两个轴)的效率会降低descendant
12赞 Palec 9/3/2016 #3

在 XPath 2.0 中,可选步骤可以表示为 。(tbody|.)

//table[@id="foo"]/(tbody|.)/tr

XPathTester.com demo

pipe () 表示(两个节点集的)并集dot () 表示标识步骤(返回前一步所做的)。|.

这可以扩展为一次包含更多可选元素:

//table[@id="foo"]/(thead|tbody|tfoot|.)/tr

评论

0赞 Palec 9/3/2016
不过,我不确定为什么这在 XPath 1.0 中不起作用。似乎应该,因为看起来像一个有效的令牌 ( → → → → → → → → → )。(tbody|.)FilterExprPrimaryExpr'(' Expr ')'OrExprAndExprEqualityExprRelationalExprAdditiveExprMultiplicativeExprUnaryExprUnionExpr