如何在XPath中引用评估XPath的节点?
E.g:
<Root>
<Foo>
<Bar><Elem>X</Elem><Ref>1</Ref></Bar>
<Bar><Elem>Y</Elem><Ref>2</Ref></Bar>
<Ref>2</Ref>
</Foo>
在Node = /Root/Foo/Ref
上执行XPath:
var myNode = GetNodeRootFooRef();
var xpath = "../Bar[Ref=.]/Elem";
myNode.SelectSingleNode(xpath); // does not work, see below
不幸的是,条件中的.
引用Bar
元素而不是我执行XPath的原始Ref
节点。如何在XPath中引用原始上下文节点?
答案 0 :(得分:4)
您需要至少XPath 2.0才能使用单个纯XPath表达式来解决它:for $current in . return ../Bar[Ref=$current]/Elem
。
Microsoft没有XPath 2.0支持,但有第三方XPath 2实现插入现有的.NET体系结构并提供扩展方法,例如, XmlNode
(需要using Wmhelp.XPath2;
和NuGet包https://www.nuget.org/packages/XPath2/1.0.2/):
string xml = @"<Root>
<Foo>
<Bar><Elem>X</Elem><Ref>1</Ref></Bar>
<Bar><Elem>Y</Elem><Ref>2</Ref></Bar>
<Ref>2</Ref>
</Foo>
</Root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode refEl = doc.SelectSingleNode("/Root/Foo/Ref");
XmlNode elem = refEl.XPath2SelectSingleNode("for $current in . return ../Bar[Ref=$current]/Elem");
Console.WriteLine(elem.OuterXml);
或者您可以使用XPath 3.0或更高版本,使用let
绑定变量let $c := . return ../Bar[Ref=$c]/Elem
的单个纯XPath表达式。
如果你想在.NET框架中的System.Xml上使用它,那么你可以安装和使用XmlPrime,它提供了扩展方法(http://www.xmlprime.com/xmlprime/doc/4.0/using-xpath.htm#extension):
string xml = @"<Root>
<Foo>
<Bar><Elem>X</Elem><Ref>1</Ref></Bar>
<Bar><Elem>Y</Elem><Ref>2</Ref></Bar>
<Ref>2</Ref>
</Foo>
</Root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode refEl = doc.SelectSingleNode("/Root/Foo/Ref");
XmlNode elem = refEl.XPathSelectSingleNode("let $c := . return ../Bar[Ref=$c]/Elem");
Console.WriteLine(elem.OuterXml);
输出<Elem>Y</Elem>
。
如果你想在.NET框架XPath API中拥有可变分辨率,那么SelectSingleNode/SelectNodes
的第二个参数是XmlNamespaceManager
,它是XsltContext
的基类,它有一个方法{ {1}} https://msdn.microsoft.com/en-us/library/system.xml.xsl.xsltcontext.resolvevariable(v=vs.110).aspx。有project on Codeplex在ResolveVariable
中实现了XsltContext
的可变分辨率,因此您可以使用它:
public class DynamicContext : XsltContext
答案 1 :(得分:2)
如果您正在使用XPath 1.0,那么您最好的选择是绑定变量,如下所示,具体取决于您的API:
var myNode = GetNodeRootFooRef();
var xpath = "../Bar[Ref=$current]/Elem";
XPath x = new XPath():
x.bindVariable("current", myNode);
x.evaluate(xpath, myNode);
但是你已经达到了XPath 1.0的极限,所以现在是时候向前迈进了。 .NET上提供了各种非常好的XPath 2.0和3.0引擎。
答案 2 :(得分:1)
如果Foo元素的结构始终相同,那么以下查询就足够了:
../Bar[Ref=(../Ref)]/Elem
(试过http://xpather.com/vjXDytGy - 这个xpath在线测试人员支持XPath 2.0)
我希望它也适用于XPath 1.0。
答案 3 :(得分:0)
../Bar[Ref=/Root/Foo/Ref]/Elem
将.
更改为原始xpath