如何为动态层次结构下的节点编写XPATH

时间:2013-01-15 20:37:33

标签: xpath

我有一个很大的XML。该XML的片段如下所示:

<div class="x-column-inner" id="ext-gen422" style="width: 850px;">
 <div id="ext-comp-1206" style="width: 14px;" class=" x-column">
  <div tabindex="-1" class="x-form-item  x-hide-label" id="ext-gen434">
   <label class="x-form-item-label" style="width:100px;" for="ext-comp-1180" id="ext-gen435"></label>
  <div style="padding-left:105px" id="x-form-el-ext-comp-1180" class="x-form-element">
   <div class="x-form-check-wrap" id="ext-gen436" style="width: 14px; height: 28px;">
    <input type="checkbox" name="ext-comp-1180" id="ext-comp-1180" autocomplete="off" class=" x-form-checkbox x-form-field">
     <label class="x-form-cb-label" for="ext-comp-1180" id="ext-gen437">&nbsp;</label>
    </div></div>  <div class="x-form-clear-left">
    </div>
   </div>
  </div>
 <div id="ext-comp-1207" style="width: 150px;" class=" x-column">
  <label id="ext-comp-1203" style="width: 140px;">Add to Watchlist</label>
</div>
<div id="ext-comp-1208" style="width: 107px;" class=" x-column">

我需要根据带有“添加到关注列表”文本的标签节点找到复选框类型的“输入”节点。

由于'input'和'label'节点位于不同的层次结构中,//语法似乎不起作用:

//div[label[contains(text(),'Add to Watchlist')]]

将只提供子标签的父div。 我试图从这个片段的最顶层节点开始

$x("//div[@class='x-column-inner' and //label[contains(text(),'Add to Watchlist')]]")

但是这给了6个可能的匹配。

注意:不能使用@id属性,因为这是动态分配给节点的,所以下次页面加载@id时会有所不同。 我不想使用position()谓词,因为这会使XPATH变为静态,而xpath可能会因位置的任何变化而中断。

1 个答案:

答案 0 :(得分:0)

你可以尝试这样的东西,但它看起来非常贪婪......基本上它的作用是在input标签的每个轴上搜索,看看是否有相关的label标签。因此,对于每个input,它会搜索其祖先,后代和兄弟姐妹。 肯定有一些更聪明的解决方案。

//input[@type = 'checkbox' and (@id = ancestor::label/@for or @id = descendant::label/@for or @id = following::label/@for or @id = preceding::label/@for)]

但是,如果您的代码段不匹配,则不会匹配input代码,请考虑提供更好的代码段。它会提高答案的准确性。

编辑:这是添加“添加到关注列表”约束的(未经测试)方式。

//input[@type = 'checkbox' and (@id = ancestor::label[. = 'Add to Watchlist']/@for or @id = descendant::label[. = 'Add to Watchlist']/@for or @id = following::label[. = 'Add to Watchlist']/@for or @id = preceding::label[. = 'Add to Watchlist']/@for)]

但是再一次,这些xpath请求非常贪婪,并且不能保证与input相关联的每个label元素匹配,例如以下input将不匹配这个片段:

<div>
  <div>
    <label for="id">Add to Watchlist</label>
  </div>
  <div>
    <input type="checkbox" id="id" />
  </div>
<div>

在一个xpath请求中可能有更高效的解决方案,但您应该考虑做几个请求 例如,一个请求使用“添加到关注列表”文本查找for元素的每个label属性值,然后执行另一个请求以查找关联的input元素。 我还应该尝试将您的请求限制在范围内的基础form元素。如果我找到时间,也许我会用更好的请求进行编辑。

编辑2 这是一个工作和更聪明的请求

//form//input[@type = 'checkbox' and @id = ancestor::form[1]//label[. = 'Add to Watchlist']/@for]

您可以将其面对此代码段

<html>
  <form>
    <label for="bar">Add to Watchlist</label>
    <div>
      <div>
        <label for="id">Add to Watchlist</label>
      </div>
      <div>
        <input type="checkbox" id="id" />
        <input type="checkbox" id="foo" />
        <input type="checkbox" id="bar" />
        <input type="checkbox" />
        <input type="checkbox" id="" />
      </div>
    </div>
  </form>
  <label for="foo">Add to Watchlist</label>
</html>

最重要的是,你了解它是如何工作的以及为什么它更好。请花点时间考虑一下。