我对why有点怀疑-至少在.Net Framework中-必须使用XmlNamespaceManager
来处理名称空间(或笨拙而冗长的[C0 ]执行XPath查询时使用XPath谓词/功能/任何内容。我do理解为什么命名空间是必要的或至少有益的,但是为什么那么复杂吗?
为了查询简单的XML文档(没有名称空间)...
[local-name()=...
...一个人可以使用类似<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode>
<nodeName>Some Text Here</nodeName>
</rootNode>
的东西(与doc.SelectSingleNode("//nodeName")
匹配)
Mystery#1:我的第一个烦恼-如果我理解正确的话–是仅将名称空间引用添加到父/根标签(是否用作子节点标签的一部分) )像这样:
<nodeName>Some Text Here</nodeName>
...需要多几行代码才能获得相同的结果:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://example.com/xmlns/foo">
<nodeName>Some Text Here</nodeName>
</rootNode>
...实质上是在梦见一个不存在的前缀(“ Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("ab", "http://example.com/xmlns/foo")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//ab:nodeName", nsmgr)
”)以查找甚至不使用前缀的节点。 这有什么意义? ab
在概念上有什么问题?
Mystery#2:因此,假设您有一个使用前缀的XML文档:
doc.SelectSingleNode("//nodeName")
...如果我正确理解,则必须将两个名称空间都添加到<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns:cde="http://example.com/xmlns/foo" xmlns:feg="http://example.com/xmlns/bar">
<cde:nodeName>Some Text Here</cde:nodeName>
<feg:nodeName>Some Other Value</feg:nodeName>
<feg:otherName>Yet Another Value</feg:otherName>
</rootNode>
中,以便对单个节点进行查询...
XmlNamespaceManager
...在这种情况下,为什么(在概念上)我需要一个名称空间管理器?
********编辑成下面的评论****
编辑添加:我经过修订和完善的问题是基于XmlNamespaceManager在大多数情况下的明显冗余以及我使用命名空间管理器来指定前缀到URI的映射的基础:
在源文档中明确声明了名称空间前缀(“ cde”)与名称空间URI(“ Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("cde", "http://example.com/xmlns/foo")
nsmgr.AddNamespace("feg", "http://example.com/xmlns/bar")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//feg:nodeName", nsmgr)
”)的直接映射:
http://example.com/xmlns/foo
程序员在进行查询之前重新创建该映射的概念需求是什么?
[...<rootNode xmlns:cde="http://example.com/xmlns/foo"...
指出的基本点是,名称空间URI是名称空间的重要部分,而不是名称空间前缀,前缀是“任意便利”]
关于为什么需要命名空间管理器,而不是使用文档可以解决这个问题,我可以想到两个原因。
如果像您的示例中那样只允许将名称空间声明添加到documentElement中,那么selectSingleNode仅使用定义的内容确实是微不足道的。
但是,您可以在文档中的任何元素上定义名称空间前缀,并且名称空间前缀并不唯一地绑定到文档中的任何给定名称空间。考虑以下示例
Kev, above
在此示例中,您想要<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>
,//z
和//a:z
返回什么?如果没有某种外部名称空间管理器,您将如何表达?
它使您可以对任何等效文档重用相同的XPath表达式,而无需了解有关所使用的名称空间前缀的任何信息。
//b:z
doc1:
myXPathExpression = "//z:y"
doc1.selectSingleNode(myXPathExpression);
doc2.selectSingleNode(myXPathExpression);
doc2:
<x>
<z:y xmlns:z="mynamespace" />
</x>
为了在没有命名空间管理器的情况下实现后一个目标,您必须检查每个文档,为每个文档构建一个自定义XPath表达式。
原因很简单。在XPath查询中使用的前缀与xml文档中声明的前缀之间没有必需的连接。举个例子,以下xml在语义上是等效的:
<x xmlns"mynamespace">
<y>
</x>
vs
<aaa:root xmlns:aaa="http://someplace.org">
<aaa:element>text</aaa:element>
</aaa:root>
“ <bbb:root xmlns:bbb="http://someplace.org">
<bbb:element>text</bbb:element>
</bbb:root>
”查询将匹配两个实例,只要在名称空间管理器中有对应关系即可。
ccc:root/ccc:element
。NET实现并不关心xml中使用的文字前缀,只是存在为查询文字定义的前缀并且名称空间值与文档的实际值匹配。即使前缀在使用的文档之间有所不同,也必须具有恒定的查询表达式,这是一般情况下的正确实现。
据我所知,如果有这样的文档,没有充分的理由应该手动定义nsmgr.AddNamespace("ccc", "http://someplace.org")
才能到达以XmlNamespaceManager
为前缀的节点:
abc
Microsoft根本不必费心编写某些东西来检测在父节点中已经指定了<itemContainer xmlns:abc="http://abc.com" xmlns:def="http://def.com">
<abc:nodeA>...</abc:nodeA>
<def:nodeB>...</def:nodeB>
<abc:nodeC>...</abc:nodeC>
</itemContainer>
。我可能是错的,如果是这样,我会欢迎对此答案发表评论,以便我进行更新。
但是,xmlns:abc
似乎证实了我的怀疑。基本上说,您需要手动定义this blog post并手动循环访问XmlNamespaceManager
属性,并将每个属性添加到名称空间管理器中。不知道为什么微软不能自动做到这一点。
这是我根据该博客帖子创建的一种方法,该方法基于源xmlns:
的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;
}
我对第1点的回答:
为XML文档设置默认名称空间仍然意味着节点,即使没有名称空间前缀,即:
XPathNavigator xNav = xmlDoc.CreateNavigator();
XPathNodeIterator xIter = xNav.Select("//abc:NodeC", createNsMgrForDocument(xmlDoc));
不再位于“空”名称空间中。您仍然需要某种方式使用XPath来引用这些节点,因此即使它是“组成”的,您也可以创建一个引用它们的前缀。
回答问题2:
<rootNode xmlns="http://someplace.org">
<nodeName>Some Text Here</nodeName>
</rootNode>
在实例文档内部,驻留在名称空间中的节点及其节点名称和长名称空间名称一起存储,在W3C中,它被称为扩展名称。
例如,<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>
本质上存储为<cde:nodeName>
。名称空间前缀对人类来说是一个任意便利,因此当我们键入XML或必须读取它时,我们不必这样做:
<http://someplace.org:nodeName>
[当搜索XML文档时,它不是通过友好前缀搜索的,它们的搜索是通过名称空间URI完成的,因此您必须通过使用<rootNode>
<http://someplace.org:nodeName>Some Text Here</http://someplace.org:nodeName>
<http://otherplace.net:nodeName>Some Other Value</http://otherplace.net:nodeName>
<http://otherplace.net:otherName>Yet Another Value</http://otherplace.net:otherName>
</rootNode>
传入的名称空间表来告知XPath有关名称空间的信息。
您需要将URI /前缀对注册到XmlNamespaceManager实例,以使SelectSingleNode()知道哪个您要引用的特定“ nodeName”节点-来自“ http://someplace.org”的一个或来自“ http://otherplace.net”的一个。
[请注意,在执行XPath查询时,具体的前缀名称并不重要。我相信这也可行:
XmlNamespaceManager
SelectSingleNode()仅需要XPath表达式的前缀与名称空间URI之间的连接。
此线程帮助我更清楚地了解名称空间的问题。谢谢。当我看到Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("any", "http://someplace.org")
nsmgr.AddNamespace("thing", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//thing:nodeName", nsmgr)
时,我尝试了一下,因为它看起来比我编写的程序更好。我发现了它的一些缺点。如所写,它仅在根节点中查找(但名称空间可以在任何地方列出。),并且它不处理默认名称空间。我试图通过修改他的代码来解决这些问题,但无济于事。
这是该功能的我的版本。它使用正则表达式在整个文件中查找名称空间映射。与默认名称空间配合使用,为其赋予任意前缀“ ns”;并处理同一名称空间的多次出现。
Jez's code