使用:
.//element[(ancestor::main|ancestor::element)[last()][self::main]]
这是使用 XSLT 进行的简单验证:
<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:for-each select=
".//element
[(ancestor::main|ancestor::element)
[last()][self::main]
]">
<xsl:copy-of select="."/>
==================
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档时:
<main>
<sub1>
<element>
<sub2>
<element>
<sub3/>
</element>
</sub2>
</element>
</sub1>
<sub4>
<sub5>
<element>
<sub6>
<element>
<sub7/>
</element>
</sub6>
</element>
</sub5>
</sub4>
<sub8>
<element/>
</sub8>
</main>
Xpath 表达式在当前节点是文档中的顶部节点的情况下进行评估,然后使用方便注意的分隔符字符串将此评估的结果复制到输出:
<element>
<sub2>
<element>
<sub3/>
</element>
</sub2>
</element>
==================
<element>
<sub6>
<element>
<sub7/>
</element>
</sub6>
</element>
==================
<element/>
==================
说明:
表达方式:
.//element
[(ancestor::main|ancestor::element)
[last()]
[self::main]
]
element
选择当前节点的所有后代元素,两个可能的祖先中的第一个(向上)main
或者element
是main
。
也就是说,没有一个element
是所选的祖先element
并且在祖先之前发生(向上)main
。
要理解为什么last()
被用作谓词而不仅仅是[1]
,我们需要记住两个祖先中最近的一个是它们中的最后一个。
请注意:
这个 XPath 表达式正确地选择了所有想要element
的元素,即使在当前节点本身有一个element
祖先的情况下也是如此。
类似于 Ian Roberts 在回答中提出的表达式:
.//element[not(ancestor::element)]
在这种情况下不选择任何内容。
更新:
正如 Michael Kay 所指出的,如果我们进一步概括这个问题并允许其他main
元素出现在文档中的任何位置,那么这个答案中提出的表达式可能会选择“误报”。
这是一个更新的表达式(不再是纯/独立的 XPath 表达式,因为它使用 XSLT 函数current()
)在这种情况下仍然产生正确的计数:
.//element
[(ancestor::main|ancestor::element)
[last()][self::main]
and
count(current()|ancestor::main[1]) = 1
]"/>