Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/xpath/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 为什么需要XmlNamespaceManager?_.net_Xpath_Xml Namespaces_Selectsinglenode - Fatal编程技术网

.net 为什么需要XmlNamespaceManager?

.net 为什么需要XmlNamespaceManager?,.net,xpath,xml-namespaces,selectsinglenode,.net,Xpath,Xml Namespaces,Selectsinglenode,关于为什么(至少在.Net框架中)有必要使用XmlNamespaceManager来处理名称空间(或者相当笨拙和冗长的[local-name()=…XPath谓词/函数/任何东西),我已经讲得有点枯燥无味了在执行XPath查询时。我是否理解为什么名称空间是必要的或至少是有益的,但是为什么它如此复杂 为了查询简单的XML文档(无名称空间) …需要多行代码才能获得相同的结果: Dim nsmgr As New XmlNamespaceManager(doc.NameTable) nsmgr.AddN

关于为什么(至少在.Net框架中)有必要使用
XmlNamespaceManager
来处理名称空间(或者相当笨拙和冗长的
[local-name()=…
XPath谓词/函数/任何东西),我已经讲得有点枯燥无味了在执行XPath查询时。我是否理解为什么名称空间是必要的或至少是有益的,但是为什么它如此复杂

为了查询简单的XML文档(无名称空间)

…需要多行代码才能获得相同的结果:

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
”),以找到一个甚至不使用前缀的节点。这有什么意义呢?
doc.SelectSingleNode(//nodeName”)

谜团2:假设您有一个使用前缀的XML文档:

<?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在我认为大多数情况下的明显冗余,以及使用命名空间管理器指定前缀到URI的映射:

当名称空间前缀(“cde”)到名称空间URI(“”)的直接映射在源文档中明确说明时:

...<rootNode xmlns:cde="http://example.com/xmlns/foo"...
…我回答第1点:

为XML文档设置默认名称空间仍然意味着节点,即使没有名称空间前缀,例如:

<rootNode xmlns="http://someplace.org">
   <nodeName>Some Text Here</nodeName>
</rootNode>

这里有一些文字
不再位于“空”命名空间中。您仍然需要某种方法使用XPath引用这些节点,因此您可以创建一个前缀来引用它们,即使它是“虚构的”

回答第2点:

<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>

这里有一些文字
其他价值
另一个价值
在实例文档内部,驻留在名称空间中的节点以其节点名和长名称空间名存储,用W3C术语称之为扩展名

例如,
基本上存储为
。命名空间前缀对人类来说是一种任意的便利,因此当我们键入XML或必须读取XML时,我们不必这样做:

<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>

这里有一些文字
其他价值
另一个价值

搜索XML文档时,它不是按友好前缀进行搜索,而是按名称空间URI进行搜索,因此您必须通过使用
XmlNamespaceManager

传入的名称空间表告诉XPath有关名称空间的信息。您需要将URI/前缀对注册到XmlNamespaceManager实例,以允许SelectSingleNode()知道您指的是哪个“节点名”节点-来自http://someplace.org“或是来自”http://otherplace.net“

请注意,在执行XPath查询时,具体前缀名称并不重要。我相信这也适用:

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)

SelectSingleNode()只需要在XPath表达式的前缀和命名空间URI之间建立连接。

据我所知,如果您有这样一个文档,那么没有理由需要手动定义一个
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>
我是这样使用它的:

<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://example.com/xmlns/foo">
   <nodeName>Some Text Here</nodeName>
</rootNode>
XPathNavigator xNav=xmlDoc.CreateNavigator();
xpathnodeiteiterator xIter=xNav.Select(“//abc:NodeC”,createNsMgrForDocument(xmlDoc));

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

<aaa:root xmlns:aaa="http://someplace.org">
 <aaa:element>text</aaa:element>
</aaa:root>
NET实现不关心xml中使用的文字前缀,只关心为查询文字定义的前缀以及名称空间值与文档的实际值匹配。这要求具有常量查询表达式,即使前缀在使用的文档之间有所不同,并且这是正确的实现对于一般情况。

基本点(如所指出的)是名称空间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>
文件1:


文件2:



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

此线程帮助我更清楚地理解了命名空间的问题。谢谢。当我看到它时,我尝试了它,因为它看起来比我编程的解决方案更好。我不过,它也弥补了一些缺点。如前所述,它只在根节点中显示(但名称空间可以在任何地方列出),并且不处理默认名称空间。我试图通过修改他的代码来解决这些问题,但没有用

这是该函数的我的版本。它使用正则表达式查找整个文件中的名称空间映射;使用默认名称空间,为它们提供任意前缀“ns”;并处理同一名称空间的多次出现

private XmlNamespaceManager CreateNamespaceManagerForDocument(XmlDocument document)
{
    var nsMgr = new XmlNamespaceManager(document.NameTable);

    // Find and remember each xmlns attribute, assigning the 'ns' prefix to default namespaces.
    var nameSpaces = new Dictionary<string, string>();
    foreach (Match match in new Regex(@"xmlns:?(.*?)=([\x22\x27])(.+?)\2").Matches(document.OuterXml))
        nameSpaces[match.Groups[1].Value + ":" + match.Groups[3].Value] = match.Groups[1].Value == "" ? "ns" : match.Groups[1].Value;

    // Go through the dictionary, and number non-unique prefixes before adding them to the namespace manager.
    var prefixCounts = new Dictionary<string, int>();
    foreach (var namespaceItem in nameSpaces)
    {
        var prefix = namespaceItem.Value;
        var namespaceURI = namespaceItem.Key.Split(':')[1];
        if (prefixCounts.ContainsKey(prefix)) 
            prefixCounts[prefix]++; 
        else 
            prefixCounts[prefix] = 0;
        nsMgr.AddNamespace(prefix + prefixCounts[prefix].ToString("#;;"), namespaceURI);
    }
    return nsMgr;
}
私有XmlNamespaceManager CreateNamespaceManager文档(XmlDocument文档)
{
var nsMgr=新的XmlNamespaceManager(document.NameTable);
//查找并记住每个xmlns属性,将“ns”前缀分配给默认名称空间。
变量名称空间=
  <bbb:root xmlns:bbb="http://someplace.org">
     <bbb:element>text</bbb:element>
  </bbb:root>
nsmgr.AddNamespace("ccc", "http://someplace.org")
<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>
myXPathExpression = "//z:y"
doc1.selectSingleNode(myXPathExpression);
doc2.selectSingleNode(myXPathExpression);
<x>
  <z:y xmlns:z="mynamespace" />
</x>
<x xmlns"mynamespace">
  <y>
</x>
private XmlNamespaceManager CreateNamespaceManagerForDocument(XmlDocument document)
{
    var nsMgr = new XmlNamespaceManager(document.NameTable);

    // Find and remember each xmlns attribute, assigning the 'ns' prefix to default namespaces.
    var nameSpaces = new Dictionary<string, string>();
    foreach (Match match in new Regex(@"xmlns:?(.*?)=([\x22\x27])(.+?)\2").Matches(document.OuterXml))
        nameSpaces[match.Groups[1].Value + ":" + match.Groups[3].Value] = match.Groups[1].Value == "" ? "ns" : match.Groups[1].Value;

    // Go through the dictionary, and number non-unique prefixes before adding them to the namespace manager.
    var prefixCounts = new Dictionary<string, int>();
    foreach (var namespaceItem in nameSpaces)
    {
        var prefix = namespaceItem.Value;
        var namespaceURI = namespaceItem.Key.Split(':')[1];
        if (prefixCounts.ContainsKey(prefix)) 
            prefixCounts[prefix]++; 
        else 
            prefixCounts[prefix] = 0;
        nsMgr.AddNamespace(prefix + prefixCounts[prefix].ToString("#;;"), namespaceURI);
    }
    return nsMgr;
}