我有以下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>

10-07 19:16
查看更多