提问人:Phrogz 提问时间:1/6/2011 最后编辑:CommunityPhrogz 更新时间:9/3/2016 访问量:15877
层次结构中带有可选元素的 XPath
XPath with optional element in hierarchy
问:
就像这个 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 表达式?
需要明确的是,我不是要解决现实世界的问题或选择特定文档的特定元素。我要求解决一类问题的技巧。
答:
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 的子元素的任何元素。tr
table
id
tr
tbody
table
评论
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
pipe () 表示(两个节点集的)并集,dot () 表示标识步骤(返回前一步所做的)。|
.
这可以扩展为一次包含更多可选元素:
//table[@id="foo"]/(thead|tbody|tfoot|.)/tr
评论
0赞
Palec
9/3/2016
不过,我不确定为什么这在 XPath 1.0 中不起作用。似乎应该,因为看起来像一个有效的令牌 ( → → → → → → → → → )。(tbody|.)
FilterExpr
PrimaryExpr
'(' Expr ')'
OrExpr
AndExpr
EqualityExpr
RelationalExpr
AdditiveExpr
MultiplicativeExpr
UnaryExpr
UnionExpr
评论
thead
tfoot