【问题标题】:XPath : Find following siblings that don't follow an order patternXPath :查找以下不遵循顺序模式的兄弟姐妹
【发布时间】:2014-10-26 03:56:33
【问题描述】:

这是用于 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>

我需要找到&lt;case&gt;s,其中&lt;break&gt; 存在于任意数量的行之后,但在下一个&lt;case&gt; 之前。

此代码仅帮助我找到那些中断没有立即跟随案例的代码:

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

..但是当我尝试使用 following-sibling::* 时,它会找到一个休息点,但不一定在下一个案例之前。

我该怎么做?

【问题讨论】:

  • 您可以使用 XPath 2.0 还是坚持使用 1.0?另外,您的第一段说您正在寻找 没有 有中断的案例陈述;你的第三段说你正在寻找那些的人。这可能相当于几乎相同的事情,但清晰会有所帮助。
  • 好点@LarsH,我更新了我的答案以反映限制。
  • 很抱歉给您带来了困惑。我试图从找到中断开始,认为我只需在语句中添加一个 NOT 来查找那些没有中断的语句。

标签: xpath axes


【解决方案1】:

我认为您正在苦苦挣扎,因为您的 XML 格式并不能很好地模拟问题。如果其他语句嵌套在 &lt;case&gt; 元素内,而不是兄弟,那么你可以使用 switch/case[break] 会容易得多。

对于您当前的结构,最简单的方法是从找到&lt;break&gt; 开始,然后向后查找匹配的&lt;case&gt;。正如@LarsH 指出的那样,我的原始表达式会找到一些附加条款。除非您将其限制为仅查找第一种情况,否则无法对其进行真正的修改来解决此问题:

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

@derp 的回答更好,可以找到有断点和没有断点的两种情况。

【讨论】:

  • 一般来说,这个 XPath 表达式可以产生有断点和没有断点的 case 语句(但后面跟着另一个有断点的 case)。
【解决方案2】:

我同意@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】:

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

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

    换句话说,这会选择满足条件的 case 元素: context 元素与下一个 break 元素之前的第一个 case 元素相同(仅考虑同级元素)。

    如果您有很多 case 语句,则此变体可能比使用 count() 更快。但是,除非您使用相关的 XPath 处理器使用相关数据对其进行测试,否则您永远无法确定。

    顺便说一句,generate-id(.) 中的. 不是必需的,因为无论如何参数默认为.。但为了便于阅读,我更喜欢明确说明。

    【讨论】:

      【解决方案4】:

      选择任何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::*)
              )
          )
      )]
      

      【讨论】:

      • 谢谢derp,我没想过要比较break相对于下一个case的位置。这帮助我解决了我的问题!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-04
      • 2012-07-23
      • 1970-01-01
      相关资源
      最近更新 更多