获取没有特定祖先 xml xpath 的节点
Posted
技术标签:
【中文标题】获取没有特定祖先 xml xpath 的节点【英文标题】:Get nodes that don't have specific ancestor xml xpath 【发布时间】:2011-05-16 01:26:37 【问题描述】:我在非常复杂的 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" 和位置()=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>
【问题讨论】:
哦,对不起,我没有意识到代码会丢失缩进和xml标签。我在这里重新粘贴所有代码。这是结构:pastebin.com/VbRBG5LA,这是 xml 文档:pastebin.com/ajPtqprf。如果有人能解决这个问题,我将不胜感激。 抱歉,不清楚您究竟想选择什么。请提供尽可能小的 XML 文档(不必是同一类型,因为您的问题似乎很笼统)只有几个级别和节点,并定义您想要选择的确切节点。请编辑您的问题,或使用更简单、更精确的定义提出新问题。 好问题,+1。请参阅我对两个 XPath 表达式的回答,它们显示了如何选择不是 XML 文档中给定元素的后代的节点。 我用更简单的例子重申了这个问题,没有任何描述。希望这有助于***.com/q/6012713/754982 是的,我几分钟前就回答了。 【参考方案1】:您没有说这是 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 的上下文)。
【讨论】:
【参考方案2】:问题不是很清楚,提供的 XML 不是格式良好的 XML 文档。
无论如何,这是我根据对这个问题文本的理解尝试回答的问题。
让我们有以下简单的 XML 文档:
<t>
<x>
<y>
<z>Text 1</z>
</y>
</x>
<x>
<y>
<z> Text 2</z>
</y>
</x>
</t>
我们要选择所有不是z
后代的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文档顶部元素t
的所有z
子元素以及顶部元素t
的任何x
子元素的所有z
后代不是第一个这样的x
孩子(其在t
的所有x
孩子中的位置不是1)。
第二个表达式的意思是:选择 XML 文档中没有一个元素 x
作为祖先的所有 z
元素,该元素只有一个元素祖先(是顶部元素)并且之前没有名为 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>
【讨论】:
很抱歉我没有很好地表达自己。我需要文档中任何地方的节点。它不必像本例中那样是t/z
是为了完整性而添加的。如果您确定z
只能出现在x
下,那么您可以省略表达式/t/z
以上是关于获取没有特定祖先 xml xpath 的节点的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Java 中使用 XPath 从 XML 中获取特定节点?