XML:声称名称空间前缀在事实上未声明

时间:2010-01-26 17:19:47

标签: asp.net xml .net-3.5 xpath namespaces

我们有一个Web服务,它返回一个非常简单的XML。

<?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'前缀有点声明。 该文件是否无效?

如果您想知道为什么我们必须首先use XmlNamespaceDeclarations to add namespace prefixes,那是因为否则生成的文档因为它有一个目标命名空间而没有得到它的前缀而被查询,所以XPath忽略节点名称,因为它们不属于请求的(空)命名空间,我们不想使用像"//*[namespace-uri()='http://our.website.com/ns' and local-name()='RequestResult']"这样的结构。

2 个答案:

答案 0 :(得分:7)

你已经回答了这个问题,但值得理解为什么会这样。

元素所在的命名空间不能仅由命名空间前缀确定。要查找名为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命名空间中。您可以告诉该文档中最深的元素位于ns-two而不是因为t:本质上意味着ns-two,但是因为如果您搜索祖先或自我轴,那么第一个元素就是你点击它的xmlns:t属性 - 它的父 - 告诉你命名空间。

鉴于此,XPath表达式//t:*应匹配哪些节点?这是不可能的,因为什么名称空间t:映射到整个文档中的更改。

此外,名称空间前缀是临时的,但名称空间是永久性的。如果你知道one位于ns-one,那么你真的不在乎它的前缀是t:还是x:,或者它根本没有前缀,只是xmlns属性。

当您使用XPath查询XML文档时,您需要一种方法来指定给定元素所在的命名空间。这就是DOMDocument中的SelectionNamespaces或C#中的命名空间管理器,或者其他什么是for:它们告诉您XPath查询中前缀代表的名称空间。因此,如果我将前缀a:设置为ns-one,则XPath //a:one会在one命名空间中找到名为ns-one的所有元素,无论如何他们在我正在搜索的文件中使用的实际前缀是什么。

当你第一次学习它时,这有点违反直觉,但实际上,这是唯一有道理的方法。

答案 1 :(得分:4)

令人惊讶的是(对我而言),这是XPath的默认行为。默认情况下,XPath查询中不允许使用名称空间前缀。

要解决此问题,必须使用DOMObject的SelectionNamespaces属性注册所需的前缀。

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

之后,可以在XPath查询中使用限定为t:的表达式。 这也解决了原来的问题,迫使我们首先使用XmlNamespaceDeclarations。