如何将XPath与没有前缀的默认命名空间一起使用?

时间:2010-03-26 16:15:25

标签: c# xpath linq-to-xml xml-namespaces

什么是XPath(在C#API中XDocument.XPathSelectElements(xpath,nsman),如果重要的话)查询此文档中的所有MyNode?

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <MyNode xmlns="lcmp" attr="true">
    <subnode />
  </MyNode>
</configuration>
  • 我尝试/configuration/MyNode这是错误的,因为它忽略了命名空间。
  • 我尝试/configuration/lcmp:MyNode这是错误的,因为lcmp是URI,而不是前缀。
  • 我尝试/configuration/{lcmp}MyNode失败,因为Additional information: '/configuration/{lcmp}MyNode' has an invalid token.

编辑:我不能像一些回答者所建议的那样使用mgr.AddNamespace("df", "lcmp");。这要求XML解析程序知道我计划提前使用的所有命名空间。由于这适用于任何源文件,我不知道手动添加前缀的命名空间。似乎{my uri}是XPath语法,但微软并没有打算实现那个......是吗?

6 个答案:

答案 0 :(得分:37)

configuration元素位于未命名的命名空间中,MyNode绑定到lcmp命名空间而没有命名空间前缀。

这个 XPATH 语句允许您在没有声明MyNode命名空间或在XPATH中使用命名空间前缀的情况下处理lcmp元素:

/configuration/*[namespace-uri()='lcmp' and local-name()='MyNode']

它匹配configuration的子元素,然后使用带有namespace-uri()local-name()函数的谓词文件管理器将其限制为MyNode元素。

如果您不知道哪些名称空间-uri将用于元素,那么您可以使 XPATH 更通用,只匹配local-name()

/configuration/*[local-name()='MyNode']

但是,您冒着匹配碰巧使用相同名称的不同词汇表(绑定到不同名称空间 - uri)中的不同元素的风险。

答案 1 :(得分:12)

您需要使用XmlNamespaceManager,如下所示:

   XDocument doc = XDocument.Load(@"..\..\XMLFile1.xml");
   XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
   mgr.AddNamespace("df", "lcmp");
   foreach (XElement myNode in doc.XPathSelectElements("configuration/df:MyNode", mgr))
   {
       Console.WriteLine(myNode.Attribute("attr").Value);
   }

答案 2 :(得分:7)

答案 3 :(得分:4)

这是一个如何使命名空间可用于XPath表达式的示例 XPathSelectElements扩展方法:

using System;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
namespace XPathExpt
{
 class Program
 {
   static void Main(string[] args)
   {
     XElement cfg = XElement.Parse(
       @"<configuration>
          <MyNode xmlns=""lcmp"" attr=""true"">
            <subnode />
          </MyNode>
         </configuration>");
     XmlNameTable nameTable = new NameTable();
     var nsMgr = new XmlNamespaceManager(nameTable);
     // Tell the namespace manager about the namespace
     // of interest (lcmp), and give it a prefix (pfx) that we'll
     // use to refer to it in XPath expressions. 
     // Note that the prefix choice is pretty arbitrary at 
     // this point.
     nsMgr.AddNamespace("pfx", "lcmp");
     foreach (var el in cfg.XPathSelectElements("//pfx:MyNode", nsMgr))
     {
         Console.WriteLine("Found element named {0}", el.Name);
     }
   }
 }
}

答案 4 :(得分:1)

Xpath 2.0 +库的示例:

using Wmhelp.XPath2;

doc.XPath2SelectElements("/*:configuration/*:MyNode");

见:

XPath and XSLT 2.0 for .NET?

答案 5 :(得分:0)

我喜欢@ mads-hansen,他的答案,以及我写这些通用实用程序类成员的好:

    /// <summary>
    /// Gets the <see cref="XNode" /> into a <c>local-name()</c>, XPath-predicate query.
    /// </summary>
    /// <param name="childElementName">Name of the child element.</param>
    /// <returns></returns>
    public static string GetLocalNameXPathQuery(string childElementName)
    {
        return GetLocalNameXPathQuery(namespacePrefixOrUri: null, childElementName: childElementName, childAttributeName: null);
    }

    /// <summary>
    /// Gets the <see cref="XNode" /> into a <c>local-name()</c>, XPath-predicate query.
    /// </summary>
    /// <param name="namespacePrefixOrUri">The namespace prefix or URI.</param>
    /// <param name="childElementName">Name of the child element.</param>
    /// <returns></returns>
    public static string GetLocalNameXPathQuery(string namespacePrefixOrUri, string childElementName)
    {
        return GetLocalNameXPathQuery(namespacePrefixOrUri, childElementName, childAttributeName: null);
    }

    /// <summary>
    /// Gets the <see cref="XNode" /> into a <c>local-name()</c>, XPath-predicate query.
    /// </summary>
    /// <param name="namespacePrefixOrUri">The namespace prefix or URI.</param>
    /// <param name="childElementName">Name of the child element.</param>
    /// <param name="childAttributeName">Name of the child attribute.</param>
    /// <returns></returns>
    /// <remarks>
    /// This routine is useful when namespace-resolving is not desirable or available.
    /// </remarks>
    public static string GetLocalNameXPathQuery(string namespacePrefixOrUri, string childElementName, string childAttributeName)
    {
        if (string.IsNullOrEmpty(childElementName)) return null;

        if (string.IsNullOrEmpty(childAttributeName))
        {
            return string.IsNullOrEmpty(namespacePrefixOrUri) ?
                string.Format("./*[local-name()='{0}']", childElementName)
                :
                string.Format("./*[namespace-uri()='{0}' and local-name()='{1}']", namespacePrefixOrUri, childElementName);
        }
        else
        {
            return string.IsNullOrEmpty(namespacePrefixOrUri) ?
                string.Format("./*[local-name()='{0}']/@{1}", childElementName, childAttributeName)
                :
                string.Format("./*[namespace-uri()='{0}' and local-name()='{1}']/@{2}", namespacePrefixOrUri, childElementName, childAttributeName);
        }
    }