【问题标题】:Is there a better way of getting parent node of XPath query result?有没有更好的方法来获取 XPath 查询结果的父节点?
【发布时间】:2012-10-04 05:19:03
【问题描述】:

有这样的标记:

<div class="foo">
   <div><span class="a1"></span><a href="...">...</a></div>
   <div><span class="a2"></span><a href="...">...</a></div>
   <div><span class="a1"></span>some text</div>
   <div><span class="a3"></span>some text</div>
</div>

如果相邻的 span 属于 a1 类,我有兴趣获取所有 &lt;a&gt;some text 。因此,在整个代码的末尾,我的结果应该是来自第一个 div&lt;a&gt; 和来自第三个的 some text。如果 &lt;a&gt;some textspandiv 中会很容易有 class 属性,但没有运气。

我现在正在做的是寻找 spana1 类:

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]

然后我得到它的父级并以该父级作为上下文节点执行另一个query()。这看起来远非高效,所以问题显然是是否有更好的方法来实现我的目标?


答案附录

根据@MarcB accepted answer,正确的查询是:

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/..

但对于&lt;a&gt;,使用它可能会更好:

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/../a

获取&lt;a&gt; 而不是它的容器。

【问题讨论】:

    标签: xml dom xpath


    【解决方案1】:

    xpath 查询的好处在于,您基本上可以将它们视为文件系统路径,因此只需拥有

    //div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/..
                                                                  ^^
    

    将找到所有低于 .foo 节点的 .a1 节点,然后向上移动一级到 a1 节点的父节点。

    【讨论】:

    • +1 用于引用文件系统路径,这就是我一直认为的方式,但我从未听说过它是这样解释的
    • 我只是在提问之前检查了手册,但似乎我设法错过了“..”,因为它显然在那里。但 FS 参考立即表明了这一点。谢谢。
    • 是的。当我第一次跳入 xpath 时,我像这样到处乱窜了一阵子,但是进行查询路径关联对我来说是相当灵光乍现的时刻。
    • 这当然是该语言被称为 X_Path_ 的原因...但是当您想了解更详细的细节时,将查询视为路径绝对没有帮助,特别是如果您想更新到 XPath 2.0 终有一天。然后 / 是某种二进制运算符评估左侧每个节点的右侧......(当我编写 XPath 解析器时,我花了数周或数月的时间将 / 作为路径分隔符)
    【解决方案2】:

    比使用反向轴更好的表达式

    //div[contains(@class,'foo')]/div[span[contains(@class,'a1')]]
    

    这会选择任何div,它是div 的子代,其class 属性包含字符串“foo”,并且(选定的div)具有span 子代,其class 属性包含字符串“a1”。

    基于 XSLT 的验证

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
      <xsl:copy-of select=
      "//div[contains(@class,'foo')]
              /div[span[contains(@class,'a1')]]"/>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时:

    <div class="foo">
       <div><span class="a1"></span><a href="...">...</a></div>
       <div><span class="a2"></span><a href="...">...</a></div>
       <div><span class="a1"></span>some text</div>
       <div><span class="a3"></span>some text</div>
    </div>
    

    计算 XPath 表达式并将所选元素复制到输出:

    <div>
       <span class="a1"/>
       <a href="...">...</a>
    </div>
    <div>
       <span class="a1"/>some text</div>
    

    二。关于通过其类之一访问 Html 元素的备注

    如果知道元素只能有一个类,那么就没有必要使用contains()

    不要使用

    //div[contains(@class, 'foo')]
    

    使用

    //div[@class = 'foo']
    

    或者,如果可能有前导/尾随空格,请使用:

    //div[normalize-space(@class) = 'foo']
    

    的一个关键问题:

    //div[contains(@class, 'foo')]
    

    这是选择任何div 的类,例如“myfoo”、“foo2”或“myfoo3”。

    如果元素可能有多个类,为了避免上述问题,正确的 XPath 表达式为

    //div[contains(concat(' ', @class, ' '), ' foo ')]
    

    【讨论】:

    • 不要忘记 html 允许多个类。 @class='foo' 将跳过 class="foo bar baz"。因此,@contains 是完全有效的,只要(如您所指出的),您注意误报
    • @MarcB,您似乎还没有阅读或理解这个答案——它详细处理了元素具有多个类的情况。此外,这个答案为这种情况提供了一个正确的解决方案——不像不正确和简单的contains(@calss, someString)
    猜你喜欢
    • 1970-01-01
    • 2014-05-14
    • 2018-02-07
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多