【问题标题】:XPath to select multiple tagsXPath 选择多个标签
【发布时间】:2010-10-17 20:10:41
【问题描述】:

鉴于这种简化的数据格式:

<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

这可能吗?

【问题讨论】:

    标签: xml xpath


    【解决方案1】:

    一个正确答案是

    /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
    

    【讨论】:

    • 'or' 不适用于 for-each,您需要使用垂直线代替 '|'
    • @Guasqueño, or 是一个逻辑运算符——它对两个布尔值进行运算。 XPath union 运算符| 在两组节点上运行。这些是完全不同的,并且每个都有特定的用例。使用| 可以解决原来的问题,但是会导致理解 XPath 表达式的时间更长、更复杂、更具挑​​战性。此答案中使用or 运算符的更简单的表达式会生成所需的节点集,并且可以&lt;xsl:for-each&gt; XSLT 操作的“选择”属性中指定。试试看吧。
    • @JonathanBenn,任何“不关心命名空间”的人实际上都不关心 XML,也不使用 XML。 local-name() 的使用只有在我们想要选择具有该本地名称的所有元素时才是正确的,而不管元素所在的命名空间。这是一种非常罕见的情况——通常人们确实关心:@987654330 之间的区别@ 和 sql:table,或介于 architecture:columnsql:columnarray:columnmilitary:column 之间
    • @DimitreNovaatchev 你说得很好。我正在使用 XPath 进行 HTML 检查,这是命名空间不那么重要的边缘情况......
    • 太棒了。你是从哪里想到的?
    【解决方案2】:

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

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

    与 Dimitre 的反对意见相反,在 OP 未指定与命名空间的交互的真空中,上述内容并非不正确self:: 轴是命名空间限制的,local-name() 不是。如果 OP 的意图是捕获 c|d|e 而不管命名空间如何(考虑到问题的 OR 性质,我认为这甚至是一种可能的情况),那么它是“另一个仍然有一些正面投票的答案”,这是不正确的。

    如果没有定义,你就无法确定,但如果 OP 澄清了他的问题,我很乐意删除我的答案是真正不正确的。

    【讨论】:

    • 在这里作为第三者发言——就我个人而言,我认为 Dimitre 的建议是更好的做法,除非用户有明确(且有充分的)理由关心与命名空间无关的标签名称;如果有人对我在不同命名空间内容中混合的文档(可能打算由不同的工具链读取)执行此操作,我会认为他们的行为非常不合适。也就是说,这个论点——正如你所建议的——有点不合时宜。
    • 正是我想要的。 XML 名称空间在现实生活中的使用方式是一团糟。由于无法指定 /a/b/(:c|:d|*e) 之类的内容,您的解决方案正是需要的。纯粹主义者可以随心所欲地争论,但用户并不关心应用程序是否崩溃,因为无论生成他们的输入文件是什么,都会搞砸命名空间。他们只是想让它发挥作用。
    • 我只有最模糊的想法这两个答案之间的区别是什么,没有人费心解释。 “名称空间限制”是什么意思?如果我使用local-name(),这是否意味着它会匹配任何命名空间的标签?如果我使用self::,它必须匹配什么命名空间?我如何只匹配OhMy:c
    【解决方案3】:

    为什么不a/b/(c|d|e)?我刚刚尝试了Saxon XML library(用一些 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>>)
    

    【讨论】:

    • 这对我来说效果很好。 XPath 2.0 似乎是 Python 2 上 lxml 中 HTML 解析的默认设置。
    【解决方案4】:

    不确定这是否有帮助,但对于 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 似乎有解决方案。
    猜你喜欢
    • 2011-09-25
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    相关资源
    最近更新 更多