XPath选择多个标签


132

给定这种简化的数据格式:

<a>
    <b>
        <c>C1</c>
        <d>D1</d>
        <e>E1</e>
        <f>don't select this one</f>
    </b>
    <b>
        <c>C2</c>
        <d>D2</d>
        <e>E1</e>
        <g>don't select me</g>
    </b>
    <c>not this one</c>
    <d>nor this one</d>
    <e>definitely not this one</e>
</a>

您将如何选择作为元素子代的所有Cs,Ds和Es B

基本上是这样的:

a/b/(c|d|e)

在我自己的情况,而不是只a/b/,查询导致到选择那些CDE节点其实是相当复杂的,所以我想避免这样做:

a/b/c|a/b/d|a/b/e

这可能吗?

Answers:


207

一个正确的答案是

/a/b/*[self::c or self::d or self::e]

请注意

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

既长又不正确。该XPath表达式将选择以下节点:

OhMy:c

NotWanted:d 

QuiteDifferent:e

2
“或”不适用于for-each,则需要使用垂直线代替“ |”
Guasqueño

8
@Guasqueño or是逻辑运算符-它对两个布尔值进行运算。XPath 联合运算符|在两组节点上进行操作。这些有很大的不同,并且每个都有特定的用例。使用| 可以解决原始问题,但是理解XPath表达式会导致更长,更复杂和更具挑战性。此答案中使用or操作符的更简单表达式生成所需的节点集,并且可以<xsl:for-each>XSLT操作的“选择”属性中指定。去尝试一下。
Dimitre Novatchev

4
@JonathanBenn,任何“不关心名称空间”的人实际上都不关心XML,也不使用XML。local-name()仅当我们要选择具有该本地名称的所有元素时才使用正确,而不管元素所在的名称空间如何。这是一种非常罕见的情况-通常,人们确实关心:kitchen:tablesql:table或之间的区别architecture:columnsql:columnarray:columnmilitary:column
Dimitre Novatchev

2
@DimitreNovatchev你说的很对。我正在使用XPath进行HTML检查,这是名称空间不是那么重要的
极端情况

2
太好了 你从哪里想出来的?
基思·泰勒

46

您可以使用属性测试来避免重复:

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

与Dimitre的反对意见相反,在OP没有指定与名称空间的交互的真空中,以上内容并不正确。该self::轴不受名称空间限制local-name()。如果OP的意图是捕获c|d|e与命名空间无关的内容(鉴于问题的OR性质,我认为这甚至是可能的情况),那么“另一个答案仍然具有一些肯定的票数”是不正确的。

如果没有定义,就不能一概而论,不过,如果OP澄清他的问题是我不正确的话,我很乐意删除我的回答,因为它确实是错误的。


3
在这里以第三方的身份发言-我个人认为Dimitre的建议是更好的做法,除非用户有明确(充分)理由关心与名称空间无关的标记名;如果有人对我混合了不同名称的内容(大概打算由其他工具链读取)的文档执行此操作,则我认为它们的行为非常不合适。就是说,正如您所建议的那样,这种说法有点不合时宜。
查尔斯·达菲

4
正是我想要的。XML名称空间在现实生活中的使用方式是一团糟。由于无法指定类似/ a / b /(:c |:d | * e)的内容,因此您的解决方案正是所需要的。纯粹主义者可以争论他们想要的一切,但用户不必担心应用程序会中断,因为无论生成什么输入文件,它们都会破坏名称空间。他们只是希望它能工作。
Ghostrider 2012年

7
我只有最模糊的想法,这两个答案之间会有什么区别,没有人愿意去解释。“限制命名空间”是什么意思?如果使用local-name(),这是否意味着它将匹配具有任何名称空间的标记?如果使用self::,它将必须匹配什么名称空间?我怎么只匹配OhMy:c
meustrus

15

为什么不a/b/(c|d|e)呢?我刚刚尝试了Saxon XML库(与Clojure的优点很好地结合在一起),而且似乎可以正常工作。 abc.xml是OP描述的文档。

(require '[saxon :as xml])
(def abc-doc (xml/compile-xml (slurp "abc.xml")))
(xml/query "a/b/(c|d|e)" abc-doc)
=> (#<XdmNode <c>C1</c>>
    #<XdmNode <d>D1</d>>
    #<XdmNode <e>E1</e>>
    #<XdmNode <c>C2</c>>
    #<XdmNode <d>D2</d>>
    #<XdmNode <e>E1</e>>)

8
是的,但这是XPath 2.0

这对我来说很好。看来的XPath 2.0是HTML解析lxml的中关于Python 2.默认
马丁·伯奇

-1

不确定是否有帮助,但是使用XSL,我会做类似的事情:

<xsl:for-each select="a/b">
    <xsl:value-of select="c"/>
    <xsl:value-of select="d"/>
    <xsl:value-of select="e"/>
</xsl:for-each>

并且此XPath不会选择B节点的所有子节点:

a/b/*

感谢Calvin,但我没有使用XSL,实际上B下还有许多我不想选择的元素。我将更新示例以使其更加清晰。
尼克

哦,在那种情况下,annakata似乎可以解决问题。
加尔文,2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.