【问题标题】:XPath: generate a relative expression from a root node to a specified node?XPath:生成从根节点到指定节点的相对表达式?
【发布时间】:2011-01-05 13:08:41
【问题描述】:

如何生成所需的 XPath 表达式以从给定的根节点遍历 xml 结构中的指定节点?

我将在运行时收到表格的 HTML 片段。我必须根据某些条件找到所需的节点,并形成从表根节点到该节点的 XPath 字符串并返回。

HTML 表格结构事先是未知的。 Java 中是否有任何 API 可以返回给定根节点和子节点的 XPath 字符串?

【问题讨论】:

  • 好问题,+1。请参阅我对生成所需 XPath 表达式的单个 XPath 2.0 表达式的回答。 :)

标签: java xpath xpath-2.0


【解决方案1】:

我建议在Groovy 中执行此操作,它提供了GPATH(本质上是 groovy 语言的 xpath 实现。)Groovy 语法非常简洁和强大,如我的blog 中所述,并且与他的 Java 无缝混合语言(groovy 被编译成 java 类文件)。

至于您要实现的目标...以下内容应遍历整个 HTML DOM 结构并搜索具有特定 id 属性(例如 unique_id_for_tag)的“标签”(例如 div),每个条目都被处理由关闭。

HTML.body.'**'.findAll {  it.name() == 'tag' && it["@id"] == 'tag_name' }.each { 
//"it" is the return value
if(it.td[0].text().toString().trim().contains('Hello')){
   var x = it.td[0].text().toString().trim();
}

【讨论】:

    【解决方案2】:

    以下是实现此目的的一种方法(我知道)

    1. 创建 XML 的 DOM
    2. 使用"//" XPATH获取指定节点的Node
    3. 一旦您从第 2 步获得了 Node 对象,那么只需使用 getParentNode() 遍历层次结构并构建 xpath

    【讨论】:

      【解决方案3】:

      这不能(仅)在纯 XPath 1.0 中完成。

      XPath 2.0 解决方案

      if(not($vStart intersect $vTarget/ancestor::*))
        then ()
        else
         for $vPath in
            string-join
                ((for $x in
                      $vTarget
                        /ancestor-or-self::*[. >> $vStart]
                          /concat(name(.),
                                  for $n in name(.),
                                      $cn in count(../*[name(.) eq $n])
                                   return
                                     if($cn ge 2)
                                       then concat('[', 
                                                     count((preceding-sibling::*
                                                                    [name() eq $n]) +1, 
                                                   ']')
                                       else (),
                                  '/'
                                     )
                     return $x),
                    ''
                 )
                 return string-join((concat(name($vStart), '/'),$vPath), '')
      

      当针对以下 XML 文档评估此 XPath 2.0 表达式时

      <table>
        <tr>
          <td><b>11</b></td>
          <td><i>12</i></td>
        </tr>
        <tr>
          <td><p><b>21</b></p></td>
          <td><p><b>221</b></p><p><b><i>222</i></b></p></td>
        </tr>
        <tr>
          <td><b>31</b></td>
          <td><i>32</i></td>
        </tr>
      </table>
      

      如果两个参数定义为

        <xsl:variable name="vStart" select="/*"/>
        <xsl:variable name="vTarget" select="/*/tr[2]/td[2]/p[2]/b/i"/>
      

      那么上面 XPath 2.0 表达式的求值结果是:

      table/tr[2]/td[2]/p[2]/b/i/
      

      【讨论】:

      • +1 好答案。我不会将位置谓词设为可选:在没有前置但有后续的目标中思考
      • @Alejandro:谢谢,我修复了表达式,当这是唯一具有该名称的孩子时,结果仍然被简化。
      【解决方案4】:

      如果您知道要选择的根元素和子元素的名称,并且只有一个具有该名称的子元素,则可以简单地使用“/root//child”。但也许我误解了你想要达到的目标。可以举个例子吗?

      【讨论】:

      • 不,它不是唯一的孩子。它可能是子级、孙子级或层次结构中的更多级别。搜索将基于节点的内容。识别出节点后,我需要获取该节点的 xpath 表达式。
      • 你可以使用类似 "/root//*[contains(.,'test')]" 的东西来测试内容,但是如果它返回多个节点,那么创建一个可能是错误的像“/root/a/b/c/child”这样的表达与第一个,因为“/root/d/e/child”也可能是一个解决方案。在这种情况下,唯一正确的 XPath 将是使用“//”...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      相关资源
      最近更新 更多