获取没有特定祖先xml xpath的节点

时间:2011-05-16 01:26:38

标签: xml xpath ancestor

我在相当复杂的xpath中挣扎了几天,我无法制定它。 我有一个来自c ++的语法树,如语言解析器,我希望有xpath查询,它选择所有不在函数名中的名称。

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

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

CALL_EXPRESSION
  DOT_EXPRESSION
    NAME_EXPRESSION
      NAME
    NAME_EXPRESSION
      NAME
  PARAMS
    NAME_EXPRESSION
      NAME

CALL_EXPRESSION
  NAME_EXPRESSION
    NAME
  PARAMS
    NAME_EXPRESSION
      NAME

ASSIGNMENT_EXPRESSION
  NAME_EXPRESSION
    NAME
  NAME_EXPRESSION
    NAME

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

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

  

//一个并[b = “CALL_EXPRESSION”] / d / A [1]

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

但是如果我像这样制定Xpath表达式,那么问题就出现了:

  

// * [B = “NAME”] [不(祖先::一个并[b = “CALL_EXPRESSION”] / d / A [1])]

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

我怀疑,问题是,祖先::只接受第一个元素(在我们的例子中是[b =“CALL_EXPRESSION”])并根据其谓词进行限制并进一步/被丢弃。所以我修改了xpath查询,如下所示:

  

// * [B = “NAME”] [不(祖先::一个[../../ B = “CALL_EXPRESSION”   和position()= 1])]

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

  

// * [B = “NAME”] [不(祖先::一个并[b = “CALL_EXPRESSION”])]

它可以假设(所有NAME都没有选择祖先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>

2 个答案:

答案 0 :(得分:2)

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

//* except //x//*

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

也可以使用等价

在XPath 1.0中模拟except运算符
E1 except E2 ==> E1[count(.|E2)!=count(E2)]

(但要注意评估E2的背景)。

答案 1 :(得分:1)

问题不是很明确,提供的XML不是格式良好的XML文档

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

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

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

我们要选择所有不属于{/ strong> 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的所有t子项以及任何z子项的所有x后代顶级元素t不是第一个x个孩子(在x的所有t个孩子中的位置不是1)。

第二个表达式意味着:选择XML文档中没有祖先的所有z元素只有一个元素祖先的元素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>