我有以下c#方法,该方法对从refNode通过xpath可访问的所有节点执行一些操作
void foo(XmlNode refNode, string xpath)
{
XmlNodeList list=refNode.SelectNodes(xpath);
//perform operation on each element of the list
}
我得到的输入xml之一是:
<A>
<B>***
<C>
<B>One</B>
</C>
<B>
<B>Two</B>
</B>
</B>
<B>...</B>
<B>...</B>
</A>
我需要选择一个refNode
<B>
(标记为***
)并将其传递给带有xpath的foo(),该xpath选择refNode的所有后代<B>
节点,但不嵌套在任何其他<B>
节点内例如,在给定的输入中,结果应包含:
1. <B>One</B>
2. <B><B>Two</B></B>
我尝试了.//B给我3个结果,而.//B[not(ancesotr::B)]返回0个结果。
我应该使用什么Xpath来获得期望的结果?
编辑
我可以更改foo方法,但不能更改其签名。此方法是库的一部分,很少有用户使用。上面给出的输入只是一个特定的实例,用户也可能将节点A发送为refnode并要求所有C节点。
编辑2
@Dimitre Novatchev的解决方案对我有用,如果我可以在foo内获取refnode的xpath而不更改其签名的情况下,或者可以通过某种方式指定
this
节点,即在其上应用xpath的节点。.//B[not(ancesotr::B) or ancesotr::B[1]=**this**]
最佳答案
使用此纯XPath 1.0表达式:
$vrefNode/descendant::B[count(ancestor::B) - count($vrefNode/ancestor::B) = 1]
其中
$vrefNode
需要用选择“引用节点”的XPath表达式替换(除非可以使用变量引用)。基于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:copy-of select=
"/*/B[1]/descendant::B[count(ancestor::B) - count(/*/B[1]/ancestor::B) = 1]"/>
</xsl:template>
</xsl:stylesheet>
在提供的XML文档上应用此转换时:
<A>
<B>***
<C>
<B>One</B>
</C>
<B>
<B>Two</B>
</B>
</B>
<B>...</B>
<B>...</B>
</A>
将评估XPath表达式,并将通过此评估选择的元素复制到输出:
<B>One</B>
<B>
<B>Two</B>
</B>