关于xml:XPath – 除了第一个特定元素之外的所有后续兄弟

XPath - All following siblings except first specific elements

假设我在查询一个XHTML文档,我想查询id='target'表后面的所有兄弟姐妹。另外,我也不希望这个特定元素的第一个

兄弟或者第一个兄弟。我能想到的最好办法是:

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

但是,这不会在应该的时候返回任何结果。显然,我不理解这方面的一些语法(我找不到很好的信息来源)。如果有更熟悉XPath语法的人能帮我一把,我当然会感激的。另外,出于纯粹的学术目的,我很好奇上面所说的到底在做什么。

更新:请参阅Larsh的答案来解释为什么我的xpath不起作用,并参阅Dimitre的答案来了解可接受的解决方案。


用途:

1
2
3
4
5
 /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兄弟姐妹,以及所有以下位置为2或更大的ol兄弟姐妹。

这正是你想要的:所有跟随兄弟姐妹,除了跟随兄弟姐妹的第一个table和跟随兄弟姐妹的第一个ol

这是纯XPath1.0,不使用任何XSLT函数。


首先回答第二个问题:上面所做的是选择所有不是tableol元素的兄弟姐妹。

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

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

如何做你想做的事:@约翰·库格曼的回答几乎是正确的,但忽略了这样一个事实,即我们必须在包括table[@id='target']之前忽略兄弟元素。我认为在纯粹的XPath1.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

更新:在XSLT1.0中可以使用解决方案-请参见@dimitre's。


当使用self::轴时,只有一个节点,所以我相信self::*[1]永远是正确的。每个节点都将是它自己的self::轴上的第一个(也是唯一的)节点。这意味着用括号括起来的表达式等价于[not(self::table) and not(self::ol)],这意味着所有表和列表都将被过滤掉。

我没有设置测试环境,但从我的角度来看,这可能会更好:

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

这将需要一些调整,但其想法是过滤掉没有前一个兄弟tabletables和没有前一个兄弟ols的ols。