XPath:查找不遵循订单模式的以下兄弟

时间:2014-09-01 23:34:27

标签: xpath axes

这是用于C代码检测。我试图标记不会中断的案例陈述。当break语句之前有多行时,树的层次结构如下所示。这是C:

中的一个例子
switch (x) {
  case 1:
    if (...) {...}
    int y = 0;
    for (...) {...}
    break;
  case 2:

它以某种方式表示为:

<switch>
  <case>...</case>
  <if>...</if>
  <expression>...</expression>
  <for>...</for>
  <break>...</break>
  <case>...</case>
</switch>

我需要找到<case><break>后任意行数,但在下一个<case>之前。

这段代码只能帮助我找到休息时间不会紧随案例的那些代码:

//case [name(following-sibling::*[1]) != 'break']

..但是当我尝试使用follow-sibling :: *时会发现一个中断,但不一定在下一个案例之前。

我该怎么做?

4 个答案:

答案 0 :(得分:2)

选择具有以下case 的任何break,不得跟随case或下一个break的位置小于case下一个count()的位置。通过在前面的兄弟姐妹上运行//case [ following-sibling::break and ( not(following-sibling::case) or ( count(following-sibling::break[1]/preceding-sibling::*) < count(following-sibling::case[1]/preceding-sibling::*) ) ) ] 来确定职位。

not()

要抓住其他案例,那些没有休息的案例,只需在其中抛出一个旧的//case [not( following-sibling::break and ( not(following-sibling::case) or ( count(following-sibling::break[1]/preceding-sibling::*) < count(following-sibling::case[1]/preceding-sibling::*) ) ) )]

{{1}}

答案 1 :(得分:1)

我认为你正在努力,因为你的XML格式并不能很好地模拟问题。如果其他语句嵌套在<case>元素中,而不是兄弟姐妹,则会更容易,那么您可以使用switch/case[break]

根据您当前的结构,最简单的方法是找到<break>,然后向后查找匹配的<case>。正如@LarsH指出的那样,我原来的表达式会找到一些额外的条款。除非您将其限制为仅查找第一种情况,否则无法对其进行修改以解决此问题:

 switch/break/preceding-sibling::case[1]

@ derp的答案更好,可以找到有和没有休息的两种情况。

答案 2 :(得分:1)

我同意@PeterHall,最好将XML重组为更接近代表C语法的抽象语法树的东西。您可以使用XSLT分组轻松地完成此操作(对于本例):

<xsl:for-each-group select="*" group-starting-with="case">
  <case>
    <xsl:copy-of select="current-group()[not(self::case)]"/>
  </case>
</xsl:for-each-group>

然后,您可以找到没有中断的案例switch/case[not(break)]

答案 3 :(得分:0)

Derp的回答是正确的。但我会添加另一个。这将选择具有中断的案例元素:

//case[generate-id(.) =
       generate-id(following-sibling::break[1]/preceding-sibling::case[1])]

换句话说,这会选择这样的case元素: context元素与下一个break元素之前的第一个case元素相同(仅考虑兄弟元素)。

如果你有很多案例陈述,这个变体可能比使用count()更快。但除非您使用相关的XPath处理器使用相关数据对其进行测试,否则您永远不会确定。

顺便说一句,.中的generate-id(.)不是必需的,因为参数默认为.。但为了便于阅读,我更愿意明确这一点。