2

我在非常复杂的 xpath 上苦苦挣扎了几天,但我无法制定它。我有一个来自 c++ 的语法树,比如语言解析器,我想要一个 xpath 查询,它选择所有不在函数名中的名称。

具体来说,我有这样的xml文档

(整个xml文档在问题的最后,它很大,我在这里粘贴文档结构的简单概述)有四种节点类型
a - 此元素包含一个节点
b - 包含节点的信息(例如“CALL_EXPRESSION ")
c - 包含实际文本(例如“printf”、变量名...)
d - 包含当前节点的后代(a 元素)

CALL_EXPRESSION
  DOT_EXPRESSION
    NAME_EXPRESSION
      姓名
    NAME_EXPRESSION
      姓名
  参数
    NAME_EXPRESSION
      姓名

CALL_EXPRESSION
  NAME_EXPRESSION
    姓名
  参数
    NAME_EXPRESSION
      姓名

ASSIGNMENT_EXPRESSION
  NAME_EXPRESSION
    姓名
  NAME_EXPRESSION
    姓名

我想制定 Xpath 查询,它将选择所有不是 CALL_EXPRESSION/*[1] 后代的名称。(这意味着我想选择所有变量而不是函数名称)。

要选择所有函数名称,我可以像这样使用 Xpath

//a[b="CALL_EXPRESSION"]/d/a[1]

这里没问题。现在,如果我想选择不是该节点后代的所有节点。我会使用 not(ancestor::X)。

但是问题来了,如果我像这样制定 Xpath 表达式:

//*[b="NAME"][not(ancestor::a[b="CALL_EXPRESSION"]/d/a[1])]

它只选择根本没有子 b="CALL_EXPRESSION" 的节点。在我们的示例中,它仅从 ASSIGNMENT_EXPRESSION 子树中选择 NAME。

我怀疑问题在于,祖先:: 仅采用第一个元素(在我们的例子中为 a[b="CALL_EXPRESSION"])并根据其谓词进行限制,并进一步 / 被丢弃。所以我修改了这样的xpath查询:

//*[b="NAME"][not(ancestor::a[../../b="CALL_EXPRESSION" and position()=1])]

这似乎只适用于更简单的 CALL_EXPRESSION(没有 DOT_EXPRESSION)。我怀疑,[] 中的路径可能仅与当前节点相关,而不与潜在祖先相关。但是当我使用查询时

//*[b="NAME"][not(ancestor::a[b="CALL_EXPRESSION"])]

它按照人们的假设工作(选择了所有没有祖先 CALL_EXPRESSION 的名称)。

有什么方法可以制定我需要的查询吗?为什么查询不起作用?

提前致谢 :)

XML

<a>
 <b>CALL_EXPRESSION</b>
 <c>object.method(a)</c>
 <d>
   <a>
     <b>DOT_EXPRESSION</b>
     <c>object.method</c>
     <d>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>object</c>
         <d>
           <a>
             <b>NAME</b>
             <c>object</c>
             <d>
             </d>
           </a>
         </d>
       </a>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>method</c>
         <d>
           <a>
             <b>NAME</b>
             <c>method</c>
             <d>
             </d>
           </a>
         </d>
       </a>
     </d>
   </a>
   <a>
     <b>PARAMS</b>
     <c>(a)</c>
     <d>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>a</c>
         <d>
           <a>
             <b>NAME</b>
             <c>a</c>
             <d>
             </d>
           </a>
         </d>
       </a>
     </d>
   </a>
 </d>
</a>

<a>
 <b>CALL_EXPRESSION</b>
 <c>puts(b)</c>
 <d>
   <a>
     <b>NAME_EXPRESSION</b>
     <c>puts</c>
     <d>
       <a>
         <b>NAME</b>
         <c>puts</c>
         <d>
         </d>
       </a>
     </d>
   </a>
   <a>
     <b>PARAMS</b>
     <c>(b)</c>
     <d>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>b</c>
         <d>
           <a>
             <b>NAME</b>
             <c>b</c>
             <d>
             </d>
           </a>
         </d>
       </a>
     </d>
   </a>
 </d>
</a>

<a>
 <b>ASSIGNMENT_EXPRESSION</b>
 <c>c=d;</c>
 <d>
   <a>
     <b>NAME_EXPRESSION</b>
     <c>c</c>
     <d>
       <a>
         <b>NAME</b>
         <c>c</c>
         <d>
         </d>
       </a>
     </d>
   </a>
   <a>
     <b>NAME_EXPRESSION</b>
     <c>d</c>
     <d>
       <a>
         <b>NAME</b>
         <c>d</c>
         <d>
         </d>
       </a>
     </d>
   </a>
 </d>
</a>
4

2 回答 2

4

您没有说这是 XPath 1.0 还是 2.0。在 XPath 2.0 中,您可以使用 except 运算符:例如

//* except //x//*

选择所有没有 x 作为祖先的元素。

except 运算符也可以在 XPath 1.0 中使用等价来模拟

E1 except E2 ==> E1[count(.|E2)!=count(E2)]

(但要注意评估 E2 的上下文)。

于 2011-05-16T09:06:49.660 回答
1

这个问题不是很清楚,并且提供的 XML 不是格式良好的 XML 文档

无论如何,这是我根据对这个问题文本的理解尝试回答的问题。

让我们有以下简单的 XML 文档

<t>
 <x>
   <y>
     <z>Text 1</z>
   </y>
 </x>
 <x>
  <y>
    <z> Text 2</z>
  </y>
 </x>
</t>

我们要选择所有z不是 /t/x[1]

使用此 XPath 表达式:

/t/z | /t/x[position() > 1]//z

或者这个:

//z[not(ancestor::x
             [count(ancestor::*) = 1
            and
              not(preceding-sibling::x)
             ]
        )
    ]

我当然会推荐第一个 XPath 表达式,因为它显然更简单、更短且更易于理解。

这意味着:选择XML 文档z顶部元素的所有子元素以及顶部元素的任何子元素的所有后代,该子元素不是第一个这样的子元素(其在所有子元素中的位置不是 1)。tzxtxxt

第二个表达式的意思是:选择zXML 文档中没有一个元素作为祖先的所有元素,该元素x只有一个元素祖先(是顶部元素的子元素)并且没有命名的前面的兄弟姐妹x(换句话说就是其父母的第一个x孩子)。

最后,这里快速验证两个 XPath 表达式的正确性

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "//z[not(ancestor::x
             [count(ancestor::*) = 1
            and
              not(preceding-sibling::x)
             ]
          )
      ]
  "/>

-------------------

 <xsl:copy-of select="/t/z | /t/x[position() > 1]//z"/>
 </xsl:template>
</xsl:stylesheet>

当这个转换应用于简单的 XML 文档(如上所示)时,我们看到两个表达式都准确地选择了想要的z元素。转换的结果是:

<z> Text 2</z>

-------------------

 <z> Text 2</z>
于 2011-05-16T02:20:49.063 回答