为什么XmlNamespaceManager是必需的?

Cod*_*key 66 .net xpath xml-namespaces selectsinglenode

我想出有点干,以为什么 -至少在.Net框架-这是必要使用XmlNamespaceManager,以处理命名空间(或相当笨重和详细[local-name()=...的XPath谓词/功能/等等)时执行XPath查询.我确实理解为什么命名空间是必要的或至少是有益的,但为什么它如此复杂?

为了查询简单的XML文档(没有命名空间)...

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode>
   <nodeName>Some Text Here</nodeName>
</rootNode>
Run Code Online (Sandbox Code Playgroud)

...可以使用类似的东西doc.SelectSingleNode("//nodeName")(可以匹配<nodeName>Some Text Here</nodeName>)

神秘#1:我的第一个烦恼 - 如果我理解正确 - 只是添加一个命名空间引用到父/ root标记(无论是否用作子节点标记的一部分),如下所示:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://someplace.org">
   <nodeName>Some Text Here</nodeName>
</rootNode>
Run Code Online (Sandbox Code Playgroud)

...需要几行额外的代码来获得相同的结果:

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("ab", "http://s+omeplace.org")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//ab:nodeName", nsmgr)
Run Code Online (Sandbox Code Playgroud)

...本质上是梦想一个不存在的前缀(" ab")来找到一个甚至不使用前缀的节点.这有什么意义?有什么问题(概念上)doc.SelectSingleNode("//nodeName")

神秘#2:所以,假设你有一个使用前缀的XML文档:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net">
   <cde:nodeName>Some Text Here</cde:nodeName>
   <feg:nodeName>Some Other Value</feg:nodeName>
   <feg:otherName>Yet Another Value</feg:otherName>
</rootNode>
Run Code Online (Sandbox Code Playgroud)

...如果我理解正确,你必须添加两个命名空间XmlNamespaceManager,以便查询单个节点...

Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("cde", "http://someplace.org")
nsmgr.AddNamespace("feg", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//feg:nodeName", nsmgr)
Run Code Online (Sandbox Code Playgroud)

...在这种情况下,为什么我需要(概念上)命名空间管理器?

**删除以下评论**

编辑添加: 我修改和改进的问题基于XmlNamespaceManager的明显冗余,我认为是大多数情况,并使用命名空间管理器来指定前缀到URI的映射:

当源文档中明确声明名称空间前缀("cde")到名称空间URI("http://someplace.org")的直接映射时:

...<rootNode xmlns:cde="http://someplace.org"...
Run Code Online (Sandbox Code Playgroud)

程序员在进行查询之前重新创建映射的概念需求是什么?

Pau*_*her 18

基本点(如上面Kev所指出的),命名空间URI是命名空间的重要部分,而不是命名空间前缀,前缀是"任意方便"

至于为什么你需要一个命名空间管理器,而不是有一些魔法使用该文档,我可以想到两个原因.

原因1

如果只允许将名称空间声明添加到documentElement,就像在示例中一样,那么selectSingleNode只使用任何已定义的内容确实是微不足道的.

但是,您可以在文档中的任何元素上定义名称空间前缀,并且名称空间前缀不会唯一地绑定到文档中的任何给定名称空间.请考虑以下示例

<w xmlns:a="mynamespace">
  <a:x>
    <y xmlns:a="myOthernamespace">
      <z xmlns="mynamespace">
      <b:z xmlns:b="mynamespace">
      <z xmlns="myOthernamespace">
      <b:z xmlns:b="myOthernamespace">
    </y>
  </a:x>
</w>
Run Code Online (Sandbox Code Playgroud)

在这个例子中,你想要什么//z,//a:z//b:z返回?如果没有某种外部命名空间管理器,你会如何表达这一点?

原因2

它允许您为任何等效文档重用相同的XPath表达式,而无需了解有关正在使用的名称空间前缀的任何信息.

myXPathExpression = "//z:y"
doc1.selectSingleNode(myXPathExpression);
doc2.selectSingleNode(myXPathExpression);
Run Code Online (Sandbox Code Playgroud)

DOC1:

<x>
  <z:y xmlns:z="mynamespace" />
</x>
Run Code Online (Sandbox Code Playgroud)

DOC2:

<x xmlns"mynamespace">
  <y>
</x>
Run Code Online (Sandbox Code Playgroud)

为了在没有命名空间管理器的情况下实现后一个目标,您必须检查每个文档,为每个文档构建自定义XPath表达式.


Adr*_*scu 13

原因很简单.您在XPath查询中使用的前缀与xml文档中声明的前缀之间没有必需的连接.举一个例子,以下xmls在语义上是等价的:

<aaa:root xmlns:aaa="http://someplace.org">
 <aaa:element>text</aaa:element>
</aaa:root>
Run Code Online (Sandbox Code Playgroud)

VS

  <bbb:root xmlns:bbb="http://someplace.org">
     <bbb:element>text</bbb:element>
  </bbb:root>
Run Code Online (Sandbox Code Playgroud)

" ccc:root/ccc:element"查询将匹配两个实例,前提是命名空间管理器中有映射.

nsmgr.AddNamespace("ccc", "http://someplace.org")
Run Code Online (Sandbox Code Playgroud)

.NET实现不关心xml中使用的文字前缀,只是为查询文字定义了前缀,并且命名空间值与doc的实际值匹配.这需要具有常量查询表达式,即使前缀在消费文档之间有所不同,并且它是一般情况下的正确实现.


Jez*_*Jez 12

据我所知,如果你有这样的文档,你没有充分的理由需要手动定义一个XmlNamespaceManager获取abc前置节点:

<itemContainer xmlns:abc="http://abc.com" xmlns:def="http://def.com">
    <abc:nodeA>...</abc:nodeA>
    <def:nodeB>...</def:nodeB>
    <abc:nodeC>...</abc:nodeC>
</itemContainer>
Run Code Online (Sandbox Code Playgroud)

微软根本无法编写可以检测xmlns:abc已在父节点中指定的内容.我可能是错的,如果是的话,我欢迎对这个答案发表评论,以便我可以更新它.

但是,这篇博文似乎证实了我的怀疑.它基本上表示您需要手动定义XmlNamespaceManager并手动迭代xmlns:属性,将每个属性添加到命名空间管理器.Dunno为什么微软不能自动执行此操作.

这是我根据博客文章创建的一种方法,可以XmlNamespaceManager根据xmlns:源的属性自动生成XmlDocument:

/// <summary>
/// Creates an XmlNamespaceManager based on a source XmlDocument's name table, and prepopulates its namespaces with any 'xmlns:' attributes of the root node.
/// </summary>
/// <param name="sourceDocument">The source XML document to create the XmlNamespaceManager for.</param>
/// <returns>The created XmlNamespaceManager.</returns>
private XmlNamespaceManager createNsMgrForDocument(XmlDocument sourceDocument)
{
    XmlNamespaceManager nsMgr = new XmlNamespaceManager(sourceDocument.NameTable);

    foreach (XmlAttribute attr in sourceDocument.SelectSingleNode("/*").Attributes)
    {
        if (attr.Prefix == "xmlns")
        {
            nsMgr.AddNamespace(attr.LocalName, attr.Value);
        }
    }

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

我这样使用它:

XPathNavigator xNav = xmlDoc.CreateNavigator();
XPathNodeIterator xIter = xNav.Select("//abc:NodeC", createNsMgrForDocument(xmlDoc));
Run Code Online (Sandbox Code Playgroud)