XML: 命名空间前缀被声明为未声明,但实际上已经声明了

4
我们有一个返回非常简单的XML的Web服务。
<?xml version="1.0"?>
<t:RequestResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://our.website.com/ns/" xmlns:t="http://our.website.com/ns/">
  <t:Result>No candy for you today.</t:Result>
  <t:Success>false</t:Success>
</t:RequestResult>

使用XMLHTTP,调用者可以轻松获取此XML。但由于"引用未声明的命名空间前缀:'t'",XPath查询无法针对此XML工作。

为什么?我会说't'前缀在某种程度上已经被声明了。 这个文档有什么问题吗?

如果你想知道为什么我们首先要使用XmlNamespaceDeclarations添加命名空间前缀,那是因为否则生成的文档不能被查询,因为它有一个目标命名空间,但没有相应的前缀,所以XPath会忽略节点名称,因为它们不属于请求(空)命名空间,并且我们不想使用像"//*[namespace-uri()='http://our.website.com/ns' and local-name()='RequestResult']"这样的构造。

2个回答

9

您已经回答了这个问题,但理解为什么很重要。

一个元素所在的命名空间不能仅通过命名空间前缀来确定。要找到一个名为t:foo的元素所在的命名空间,必须沿着祖先或自身轴向上搜索,直到找到定义t:命名空间的最近节点为止。例如:

<t:one xmlns:t="ns-one">
   <t:one>
      <t:two xmlns:t="ns-two">
         <t:two/>
      </t:two>
   </t:one>
</t:one>

在这个文档中,每个名为one的元素都在命名空间ns-one中,每个名为two的元素都在命名空间ns-two中。你可以通过搜索祖先或自身轴,找到具有xmlns:t属性的第一个元素的父元素来确定该文档中最深的元素位于ns-two,而不是因为t:本质上意味着ns-two
鉴于此,XPath表达式//t:*应匹配哪些节点?无法确定,因为t:映射到的命名空间会在整个文档中发生变化。
此外,命名空间前缀是临时的,但命名空间是永久的。如果您知道onens-one中,那么您真正关心的是它的前缀是t:还是x:,或者它根本没有前缀,只有一个xmlns属性。
当您使用XPath查询XML文档时,需要一种指定给定元素所在命名空间的方法。这就是DOMDocument中的SelectionNamespaces或C#中的命名空间管理器等用途:它们告诉您XPath查询中前缀表示的命名空间。因此,如果我将前缀a:设置为ns-one,则XPath //a:one将找到所有命名为one的元素,无论我要搜索的文档中实际使用的前缀是什么。
当您首次学习时,这可能有点反直觉,但实际上这是唯一有意义的方法。

我想把这个回答设为被接受的答案。但它只涉及到实际的答案,所以为了方便未来的读者,您能否将我的答案合并到您的答案中,这样我就可以将其作为一个整体接受呢? - GSerg

4

令我惊讶的是,这是XPath的默认行为。默认情况下,在XPath查询中不允许使用命名空间前缀。

要解决这个问题,需要通过DOMObject的SelectionNamespaces属性注册所需的前缀。

objXML.setProperty("SelectionNamespaces", "xmlns:t='http://our.website.com/ns/'")

之后,可以在XPath查询中使用带有t:限定的表达式。这也解决了我们最初不得不使用XmlNamespaceDeclarations的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接