我在相当复杂的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>
答案 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>