c# - "Namespace ' x ' is not defined"尽管先调用 GetNamespacesInScope

标签 c# .net xml xpath

我正在尝试使用 xPathNavigator 在 c# .NET 中解析有效的 XML 文档。 XML 文档的开头如下所示:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <ns1:myResponse xmlns:ns1="ExampleNS" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <myReturn href="#2" />

如您所见,在根元素上定义了一些命名空间,但是 ns1 是稍后定义的。当我尝试评估 xPath 查询时 /soapenv:Envelope/soapenv:Body/ns1:myResponse/myReturn 我得到一个 XPathException:

Namespace prefix 'ns1' is not defined.

我所做的是将 XML 文档作为字符串获取,并将其加载到 XmlDocument 对象中。然后我创建一个导航器,移动到根元素并调用 GetNamespacesInScope(XmlNamespaceScope.All)。我遍历这个集合,它包含 xmlsoapenvxsdxsi 的命名空间,如root,将它们添加到命名空间管理器。然后我创建一个 xPathNavigator 并调用 Evaluate,并获取异常。

代码是这样的:

string response = GetXMLString();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(response);

var xmlDocumentNavigator = xmlDocument.CreateNavigator();
xmlDocumentNavigator.MoveToFollowing(XPathNodeType.Element);
var xnm = new XmlNamespaceManager(xmlDocument.NameTable);
var namespacesInScope = xmlDocumentNavigator.GetNamespacesInScope(XmlNamespaceScope.All);
if (namespacesInScope != null)
{
    foreach (var prefix in namespacesInScope.Keys)
    {
        xnm.AddNamespace(prefix, namespacesInScope[prefix]);
    }
}

var xPathDocument = new XPathDocument(new StringReader(response));
var xPathNavigator = xPathDocument.CreateNavigator();

xPathNavigator.Evaluate("/soapenv:Envelope/soapenv:Body/ns1:myResponse/myReturn", xnm);

为什么 GetNamespacesInScope 方法在 ns1 上不可用?

最佳答案

问题是 GetNamespacesInScope 只返回导航器当前节点范围内的命名空间(我猜该方法建议:)。

获取所有命名空间的一种技巧是遍历所有元素,获取命名空间,然后仅重新组合唯一的元素。

字典合并代码courtesy of JonSkeet

 XPathDocument xpd = new XPathDocument(new StringReader(s));
 XPathNavigator xpn = xpd.CreateNavigator();
 List<IDictionary<string, string>> xmlnsList = new List<IDictionary<string,string>>();
 while (xpn.MoveToFollowing(XPathNodeType.Element))
 {
     xmlnsList.Add(xpn.GetNamespacesInScope(XmlNamespaceScope.All));
 }
 var result = xmlnsList.SelectMany(dict => dict)
              .ToLookup(pair => pair.Key, pair => pair.Value)
              .ToDictionary(group => group.Key, group => group.First());
 if (result != null)
 {
     foreach (var prefix in result.Keys)
     {
         xnm.AddNamespace(prefix, result[prefix]);
     }
 }

编辑

根据 Paciv 的评论,如果您确实事先不知道 xml 文档中的命名空间是什么,另一种方法是使用与命名空间无关的 Xpath,并避免围绕嗅探命名空间的问题(并消除对命名空间的需求经理)。

在您的示例中,这将是:

xPathNavigator.Evaluate("/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='myResponse']/*[local-name()='myReturn']");

关于c# - "Namespace ' x ' is not defined"尽管先调用 GetNamespacesInScope,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12390927/

相关文章:

c# - 在设计时设置 ComboBox 的 Text 和 Value 属性

c# - .NET 写一个文件,附加文件版本信息

javascript - 我如何使用 jquery ajax() 和 $.when() 方法从 .xml 文档获取 xml 节点文本/值

c# - 如何在 Entity Framework 中使用扩展方法

.net - 当附加调试器时,从 .Net 代码调用 C++ dll 的速度极其缓慢

xml - XSLT 在输出中包含空元素

javascript - 使用 javascript 将 class 和 id 属性添加到 HTML 标签

c# - 类属性的 Lambda 表达式

c# - 允许选择文本的 WPF 文本 block

c# - Windows 窗体 TreeView - 绑定(bind)分层递归数据源