如何在SelectSingleNode中使用fn:max

时间:2010-07-12 11:28:59

标签: c# .net xpath

当我运行时:

XmlDocument xmlResponse = new XmlDocument();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlResponse.NameTable);
nsmgr.AddNamespace("fn", " http://www.w3.org/2005/xpath-functions");

xmlResponse.LoadXml(
    "<LIST>" + 
        "<ITEM NUMBER='3' TEXT='C'/>" + 
        "<ITEM NUMBER='2' TEXT='B'/>" + 
        "<ITEM NUMBER='1' TEXT='A'/>" + 
    "</LIST>");

XmlNode xmlNode = xmlResponse.SelectSingleNode("//ITEM[fn:max(@NUMBER)]", nsmgr);

我得到一个异常“由于函数未知,此查询需要XsltContext。”在最后一行。我正在尝试选择具有最高NUMBER属性的ITEM元素。这可能使用XPATH吗?

我使用的是.Net 2.0,Linq不是一个选项。

由于

3 个答案:

答案 0 :(得分:4)

XmlNode xmlNode = xmlResponse.SelectSingleNode("//ITEM[fn:max(@NUMBER)]", nsmgr);

max()是一个XPath 2.0函数。 .NET仅支持XPath1.0。

在XPath 1.0中,您可以使用:

/*/ITEM[not(@NUMBER > ../@NUMBER)]

即使.NET已经实现了XPath 2.0,问题中的XPath表达式也不会选择具有最大ITEM属性的NUMBER。使用max()选择它的正确XPath 2.0表达式是:

/*/ITEM[xs:integer(@NUMBER) eq max(../@NUMBER/xs:string(.))]

这是因为max()的参数必须是我们需要确定最大元素的项目序列。相反,在问题的XPath表达式中:

//ITEM[fn:max(@NUMBER)]

max只有一个参数 - 上下文节点的NUMBER属性。因此,上述内容相当于:

//ITEM[@NUMBER]

选择具有ITEM属性的所有NUMBER元素。

答案 1 :(得分:2)

您可以使用以下机制查找具有任何给定属性的最高值的节点:

XmlNode xmlNode = xmlResponse.SelectSingleNode( "//ITEM[not(preceding-sibling::ITEM/@NUMBER > @NUMBER or following-sibling::ITEM/@NUMBER > @NUMBER)]", nsmgr ); 

在XPath中,您可以实现自己的XsltContext并将其传递给XPathExpression.SetContext()方法。 每次XPath都会使用未知函数时调用此XstlContext,这是使用自定义函数和变量扩展XPath的方法。

答案 2 :(得分:1)

如上所述,Microsoft .NET框架XML API(如XmlDocument和XPathDocument / XPathNavigator)仅支持XPath 1.0,而您要使用的max函数是XPath 2.0。 要查找具有最大值的元素,可以使用Microsoft API进行另一种解决方案,即排序(按降序排列最大值)和按排序顺序访问第一项:

XmlDocument xmlResponse = new XmlDocument();
xmlResponse.LoadXml(
        "<LIST>" +
            "<ITEM NUMBER='3' TEXT='C'/>" +
            "<ITEM NUMBER='2' TEXT='B'/>" +
            "<ITEM NUMBER='1' TEXT='A'/>" +
        "</LIST>");

XPathNavigator nav = xmlResponse.CreateNavigator();
XPathExpression exp = nav.Compile("LIST/ITEM");
exp.AddSort("@NUMBER", XmlSortOrder.Descending, XmlCaseOrder.None, "", XmlDataType.Number);
XmlNode item = nav.SelectSingleNode(exp).UnderlyingObject as XmlNode;
Console.WriteLine(item.OuterXml);