XPath - 除了第一个特定元素之外的所有后续兄弟

时间:2010-11-29 04:14:30

标签: xml xhtml xpath

假设我正在查询xhtml文档,并且我想查询带有id='target'的表后面的所有兄弟姐妹。此外,我既不想要第一个<table>兄弟,也不想要这个特定元素的第一个<ol>兄弟。这是我能想到的最好的:

//table[@id='target']/following-sibling::*[not(self::table[1]) and not(self::ol[1])]

然而,这应该没有返回任何结果。显然我不理解这方面的一些语法(我找不到一个好的信息来源)。如果有经验丰富的XPath语法的人能帮到我,我当然会感激不尽。另外,出于纯粹的学术目的,我很好奇上面的内容是什么。

更新:
请参阅LarsH的答案,解释为什么我的XPath无法正常工作,并看到Dimitre对已接受解决方案的回答。

3 个答案:

答案 0 :(得分:14)

使用

 /table[@id='target']/following-sibling::*[not(self::table) and not(self::ol)] 
| 
 /table[@id='target']/following-sibling::table[position() > 1]
|
 /table[@id='target']/following-sibling::ol[position() > 1]

这将选择表格中不是table且不是ol的所有以下兄弟姐妹以及位置为2或更高且以下所有table所有兄弟姐妹ol位置为2或更大的兄弟姐妹。

这正是您想要的:所有以下兄弟姐妹,除了第一个table后续兄弟姐妹和第一个ol以下兄弟姐妹。

这是纯XPath 1.0 ,并且不使用任何XSLT函数。

答案 1 :(得分:2)

首先回答第二个问题:上面做的是选择以下既不是table也不是ol元素的兄弟姐妹。

原因如下:self::table[1]选择上下文节点的self(如果它通过table元素名称测试)并过滤以仅选择沿self :: axis的第一个节点。 self :: axis上最多有一个节点通过元素名称测试,因此[1]是多余的。 self::table[1]只要它是一个表元素就选择上下文节点,无论它在兄弟节点中的位置如何。因此,只要上下文节点是表元素,not(self::table[1])就会返回false,无论它在兄弟节点中的位置如何。

同样适用于self::ol[1]

如何做你想做的事情: @John Kugelman的答案几乎是正确的,但忽略了我们必须忽略兄弟元素table[@id='target']之前和之后的事实。我不认为在纯XPath 1.0中正确执行是可能的。 您是否可以使用XPath 2.0?如果您在浏览器中工作,答案通常是否定的。

一些解决方法是:

  • 通过过滤其他一些基础,例如属性,跳过下面的第一个兄弟姐妹和第一个跟随兄弟姐妹;
  • 选择//table[@id='target']作为节点集,将其返回到主机环境(即XPath之外,例如在JavaScript中),循环通过该节点集;循环内部:通过XPath选择following-sibling::*,遍历XPath之外的 ,并测试每个结果(在XPath之外)以查看它是第一个表还是ol。
  • 选择//table[@id='target']作为节点集,将其返回到主机环境(即XPath之外,例如在JavaScript中),循环通过该节点集;在循环内:通过XPath选择generate-id(following-sibling::table[1])generate-id(following-sibling::ol[1]),将这些值接收到JS变量t1id和o1id中,并使用'following-sibling::*[generate-id() != ' + t1id + ' and generate-id() != ' + o1id + ']'形式为XPath表达式构造一个字符串。在XPath中选择该字符串,你就有了答案! :-P

更新:在XSLT 1.0中可以使用 解决方案 - 请参阅@ Dimitre's。

答案 2 :(得分:1)

当您使用self::轴时,只会有一个节点,所以我相信self::*[1]将始终为真。每个节点将成为其自己的self::轴上的第一个(也是唯一的)节点。这意味着您的括号表达式等同于[not(self::table) and not(self::ol)],这意味着所有表格和列表都会被过滤掉。

我没有设置测试环境,但是我可能会做得更好:

/table[@id='target']/following-sibling::*
    [not(self::table and not(preceding-sibling::table)) and
     not(self::ol    and not(preceding-sibling::ol))]

需要进行一些调整,但想法是过滤掉没有先前兄弟table的{​​{1}}和没有前面的table兄弟ol s。