我有以下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>
(标记为***
)并将其传递给foo(),其中xpath选择refNode的所有后代<B>
节点,但不嵌套在里面任何其他<B>
节点
例如,在给定的输入中,结果应包含:
1. <B>One</B>
2. <B><B>Two</B></B>
我试过.//B给了我3个结果而.//B [not (ancesotr :::))]返回0结果。
我应该使用什么Xpath来获得所需的结果?
修改
我可以对方法foo进行更改,但不能对其签名进行更改。此方法是库的一部分,并且正由少数用户使用。上面给出的输入只是一个特定的实例,用户也可以将节点A作为refnode发送并请求所有C节点。
EDIT2
@Dimitre Novatchev的解决方案适用于我,如果我可以在foo中获取refnode的xpath而不更改其签名,或者是否有某种方式来指定this
节点,即应用xpath的节点。
.//B[not(ancesotr::B) or ancesotr::B[1]=**this**]
答案 0 :(得分:2)
使用此纯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>
答案 1 :(得分:0)
我不确定纯XPath可以做到这一点。 XQuery具有更强的表达能力,例如,您可以轻松使用let
来保存B
元素,其子树在变量中搜索更多B
个后代,然后您可以确保它是只有B
祖先。
所以这是一个XQuery示例:
let $inp := <A>
<B>
***
<C>
<B>One</B>
</C>
<B>
<B>Two</B>
</B>
<D>
<E>
<B>Three</B>
</E>
</D>
<D>
<B>Four<Foo>
<B>Five</B>
</Foo>
</B>
</D>
</B>
<B>...</B>
<B>...</B>
</A>
let $b := $inp/B[1]
return $b/descendant::B[count(ancestor::B) = 1 and ancestor::B[1] is $b]
找到节点
<B>One</B>
<B>
<B>Two</B>
</B>
<B>Three</B>
<B>Four<Foo>
<B>Five</B>
</Foo>
</B>
.NET框架有XQuery实现,如XmlPrime,Saxon 9 .NET版本,AltovaXML或http://qm.codeplex.com/。
答案 2 :(得分:0)
一个xpath,它选择refNode的所有后代
<B>
节点,但不嵌套在任何其他<B>
节点内
在XSLT中你可以使用
.//B[generate-id(ancestor::B[1]) = generate-id(current())]
这将选择当前上下文节点的所有B
个后代,其最近的封闭B
祖先是当前上下文节点本身。但这取决于generate-id()
和current()
,它们是特定于XSLT的,而不是普通XPath的一部分。
以下是两步替代方案:
foo(refNode, ".//B[count(ancestor::B) = " + refNode.SelectNodes("ancestor-or-self::B").Count + "]");
动态生成XPath表达式。这将找到与B
具有相同refNode
个祖先B
的所有refNode
个后代(加上一个,如果refNode
本身为{{1}元素),它具有排除多个“B”层深的B
后代的效果。
答案 3 :(得分:0)
你必须使用XPath吗?如果您可以使用Linq-to-XML,
XElement refNode = ...
XElement wantedBs = refNode.Descendants("B")
.Where(b => parent.Name.LocalName != "B");