在C#中使用带有默认命名空间的Xpath

mac*_*ojw 61 c# xml xpath namespaces xpathnavigator

我有一个带有默认命名空间的XML文档.我正在使用XPathNavigator使用Xpath选择一组节点,如下所示:

XmlElement myXML = ...;  
XPathNavigator navigator = myXML.CreateNavigator();
XPathNodeIterator result = navigator.Select("/outerelement/innerelement");
Run Code Online (Sandbox Code Playgroud)

我没有得到任何结果:我假设这是因为我没有指定命名空间.如何在我的选择中包含命名空间?

Mar*_*ell 80

首先 - 你不需要导航仪; SelectNodes/SelectSingleNode应该足够了.

但是,您可能需要命名空间管理器 - 例如:

XmlElement el = ...; //TODO
XmlNamespaceManager nsmgr = new XmlNamespaceManager(
    el.OwnerDocument.NameTable);
nsmgr.AddNamespace("x", el.OwnerDocument.DocumentElement.NamespaceURI);
var nodes = el.SelectNodes(@"/x:outerelement/x:innerelement", nsmgr);
Run Code Online (Sandbox Code Playgroud)

  • 注意:将别名设置为空字符串(`nsmgr.AddNamespace("",el.OwnerDocument.DocumentElement.NamespaceURI);`)使其成为默认命名空间.但是,遗憾的是,这并不意味着您可以在不使用前缀的情况下使用XPath(例如`var nodes = el.SelectNodes(@"/ outerelement/innerelement",nsmgr);`).只有你可以使用`nsmgr.DefaultNamespace`看到这个.更多信息:http://stackoverflow.com/a/4271875/361842.如果要避免使用前缀,添加注释以节省其他时间; 即你不能. (5认同)

Che*_*eso 48

您可能想尝试使用XPath Visualizer工具来帮助您完成.

XPathVisualizer是免费的,易于使用.

替代文字

重要说明:如果您使用的是Windows 7/8但未看到"文件","编辑"和"帮助菜单"项,请按ALT键.


Mit*_*lik 26

对于那些寻找快速入侵解决方案的人来说,特别是在你知道 XML并且不需要担心命名空间的情况下,你可以通过简单地将文件读取到一个字符串来解决这个烦人的小"功能".取代令人反感的属性:

XmlDocument doc = new XmlDocument();
string fileData = File.ReadAllText(fileName);
fileData = fileData.Replace(" xmlns=\"", " whocares=\"");
using (StringReader sr = new StringReader(fileData))
{
   doc.Load(sr);
}

XmlNodeList nodeList = doc.SelectNodes("project/property");
Run Code Online (Sandbox Code Playgroud)

我发现这比在处理单个文件时需要默认命名空间的前缀的所有其他非意义更容易.希望这可以帮助.

  • 这太棒了.关于处理XmlNamespaceManager的所有其他BS都是无用的.10,000个中的9999个你知道XML. (8认同)
  • 这个不错的正则表达式`string filter = @"xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)"" "; fileData = Regex.Replace(fileData, filter, "");` 我在这里找到了 https://techoctave.com/c7/posts/113-c-reading-xml-with-namespace (3认同)
  • “废话”与单个文件无关——它与命名空间标签有关。如果您可以控制 XML,那么您就不必使用名称空间(标签将存在于空名称空间中)。如果您无法控制,那么您正在为需要 1/2 代码的解决方案创建一个 hack。并且 Timothy 指出,现在您将有两种不同的解决方案,这取决于您是否可以抓住不重复的标签。因为你想省两行,用了四行。 (2认同)
  • @杰拉德-我不是想让别人知道。我的帖子与KISS有关,而不是嘲笑。无论如何:(1)我称我的解决方案为hack,这暗示它不是“正确”的方法;(2)无论我的听众是否控制XML,我都明确指出,这仅是在了解XML且无需担心名称空间的情况下才是好的解决方案。(3)确实可能只需要几条额外的行就可以包含一个管理器并指定名称空间,但是XPath字符串本身最终看起来确实很混乱,所有额外的名称空间杂乱无章。 (2认同)

Tom*_*icz 20

在具有命名空间的XML上使用.NET中的XPath(通过导航器或SelectNodes/SelectSingleNode)时,您需要:

  • 提供您自己的XmlNamespaceManager

  • 显式地在XPath表达式中添加所有元素,这些元素位于命名空间中.

后者是(从下面链接的MS源转述):因为XPath 1.0忽略默认命名空间规范(xmlns ="some_namespace").因此,当您使用不带前缀的元素名称时,它假定为null namespace.

这就是为什么.NET实现XPath忽略了XmlNamespaceManager中前缀为String.Empty的命名空间,并且总是使用null命名空间.

有关详细信息,请参阅XmlNamespaceManager和UndefinedXsltContext不处理默认命名空间.

我发现这个"功能"非常不方便,因为你不能通过简单地添加默认命名空间声明来使旧的XPath命名空间感知,但这就是它的工作原理.


小智 6

您可以在不使用XmlNamespaceManager的情况下使用XPath语句,如下所示:

...
navigator.Select("//*[ local-name() = 'innerelement' and namespace-uri() = '' ]")
...
Run Code Online (Sandbox Code Playgroud)

这是在XML中选择默认命名空间的元素的简单方法.

重点是使用:

namespace-uri() = ''
Run Code Online (Sandbox Code Playgroud)

这将找到具有默认命名空间的元素而不使用前缀.

  • namespace-uri='' 对我不起作用,但它给了我动态创建 xpath 表达式的想法,如下所示: doc.SelectNodes(String.Format("///*[local-name()='innerelement'和 namespace-uri()='{0}']", doc.DocumentElement.NamespaceURI)); 这有效 (2认同)

Ken*_*ent 6

我的回答扩展了布兰登之前的回答.我用他的例子创建了一个扩展方法,如下所示:

static public class XmlDocumentExt
{
    static public XmlNamespaceManager GetPopulatedNamespaceMgr(this System.Xml.XmlDocument xd)
    {
        XmlNamespaceManager nmsp = new XmlNamespaceManager(xd.NameTable);
        XPathNavigator nav = xd.DocumentElement.CreateNavigator();
        foreach (KeyValuePair<string,string> kvp in nav.GetNamespacesInScope(XmlNamespaceScope.All))
        {
            string sKey = kvp.Key;
            if (sKey == "")
            {
                sKey = "default";
            }
            nmsp.AddNamespace(sKey, kvp.Value);
        }

        return nmsp;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的XML解析代码中,我只添加一行:

XmlDocument xdCandidate = new XmlDocument();
xdCandidate.Load(sCandidateFile);
XmlNamespaceManager nmsp = xdCandidate.GetPopulatedNamespaceMgr();  // 1-line addition
XmlElement xeScoreData = (XmlElement)xdCandidate.SelectSingleNode("default:ScoreData", nmsp);
Run Code Online (Sandbox Code Playgroud)

我非常喜欢这种方法,因为它在从源XML文件加载命名空间方面是完全动态的,并且它并没有完全忽略XML命名空间的概念,因此这可以与需要多个命名空间的XML一起用于解除冲突.


Ras*_*dit 5

如果名称空间因outerelement和innerelement而异

XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable);
                            manager.AddNamespace("o", "namespaceforOuterElement");
                            manager.AddNamespace("i", "namespaceforInnerElement");
string xpath = @"/o:outerelement/i:innerelement"
// For single node value selection
XPathExpression xPathExpression = navigator.Compile(xpath );
string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText;

// For multiple node selection
XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager);
Run Code Online (Sandbox Code Playgroud)


Bra*_*don 5

我遇到了类似的空白默认命名空间问题.在这个示例XML中,我有一些带有名称空间前缀的元素和一个没有的单个元素(DataBlock):

<src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b">
 <DataBlock>
  <a:DocID>
   <a:IdID>7</a:IdID>
  </a:DocID>
  <b:Supplimental>
   <b:Data1>Value</b:Data1>
   <b:Data2/>
   <b:Extra1>
    <b:More1>Value</b:More1>
   </b:Extra1>
  </b:Supplimental>
 </DataBlock>
</src:SRCExample>
Run Code Online (Sandbox Code Playgroud)

我试图使用在XPath Visualizer中工作的XPath,但在我的代码中不起作用:

  XmlDocument doc = new XmlDocument();
  doc.Load( textBox1.Text );
  XPathNavigator nav = doc.DocumentElement.CreateNavigator();
  XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable );
  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
    nsman.AddNamespace( nskvp.Key, nskvp.Value );
  }

  XPathNodeIterator nodes;

  XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" );
  failingexpr.SetContext( nsman );
  nodes = nav.Select( failingexpr );
  while ( nodes.MoveNext() ) {
    string testvalue = nodes.Current.Value;
  }
Run Code Online (Sandbox Code Playgroud)

我将其缩小到XPath的"DataBlock"元素,但除了通配简单的DataBlock元素之外无法使其工作:

  XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" );
  failingexpr.SetContext( nsman );
  nodes = nav.Select( failingexpr );
  while ( nodes.MoveNext() ) {
    string testvalue = nodes.Current.Value;
  }
Run Code Online (Sandbox Code Playgroud)

经过大量的头部搜索和谷歌搜索(让我来到这里)后,我决定直接在我的XmlNamespaceManager加载器中处理默认命名空间,方法是将其更改为:

  foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
    nsman.AddNamespace( nskvp.Key, nskvp.Value );
    if ( nskvp.Key == "" ) {
      nsman.AddNamespace( "default", nskvp.Value );
    }
  }
Run Code Online (Sandbox Code Playgroud)

所以现在"默认"和""指向相同的命名空间.一旦我这样做,XPath"/ src:SRCExample/default:DataBlock/a:DocID/a:IdID"就像我想要的那样返回了我的结果.希望这有助于澄清其他人的问题.