`
ext2xhb
  • 浏览: 9585 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

XML Namespace微妙问题

阅读更多
XML相关的规范几乎都使用namespace限定XML数据.由于namespace有很多微妙的细节,如果对这些微妙的细节不能有清晰的把握,往往会导致一些难以理解的错误。在这里我把个人遇到的一些问题进行了一下归纳。分为以下几个方面:

1)xml namespace的基本问题:
这一部分描述了namespace的基本知识以及一些微妙的细节,是最为基础和关键的部分。其他部分不过是其他扩展使用而已。黑色字体标注出了较为微妙的问题的标题。

2)Schema中对namespace使用
3)XPath中的namespace使用
4)Webservice的namespace注意问题


1:XML的namespace的基本问题

1.1:xml元素的限定名(QName)

大多数xml初学者一开始接触xml时,被灌输的xml元素名都是非限定名称。这也是导致我们经常在namespace问题上犯错误的根本原因。

使用限定名的示例:
<ns:Book xmlns:ns="http:// publication"/>

xml元素为限定名,包括两个部分:namespace + localPart;
上述例子中namespace的为 "http:// publication", localPart为Book。

打个比方,namespace的作用是将不同的xml元素划分为不同的集合。集合的名称就是namespace的uri值。 元素自己的名称就是集合内的名称。所以判定2个xml元素的名称一样的条件必须是:xml元素所属集合(namespace) 以及集合内的名字(xml元素自己的名字)都一样。

1.1.1:如何在QName的框架下,理解非限定名?

下面是一个与我们最初接触的xml类似的示例.
<book>.....</book>
这样一个例子就是非限定名称的xml元素。那么在限定名称(QName)的框架下如何理解非限定名称呢? 我们可以把非限定名的xml元素理解为 namespace的uri值为""(空串,以后我们简述为空), localPart=xml元素名。换句话说,非限定名 是namespace的uri值为空的QName的特殊称谓。

后面我们会进一步描述这种情况的一些微妙细节。


1.2:xml中 如何定义和使用namespace
基本规则如下:
a)使用形式为xmlns:prefix="uri"的特殊xml属性定义namespace
b)在元素名称前使用 prefix:来修饰元素名,表示元素QName的namespace
示例:<ns:Book xmlns:ns="http:// publication "/>
ns: 是prefix. 表示uri(http://publish/a)的缩写
ns:Book:是对Book的修饰。表示Book位于名字空间”http://publish/a。

prefix可以省略,但省略后会引发一些微妙的问题,后面我们会详细描述这种情况

prefix只是指代namespace的一个缩略词,本身没有任何实际的意义。所以缩略词是什么,根本不影响xml数据的实际意义。
示例:
<ns:Book xmlns:ns=”http://publication”/>
<pub:Book xmlns:pub="http://publication”/>
虽然看上去不太一样,这两个XML数据完全等价。

这意味着,我们写程序判断两个xml是否相等时,不能只是简单的判断xml的元素名是否相同,而必须判断QName是否相同。如何正确的获取xml的QName取决与我们所使用的具体的XML 数据访问API。对于w3c接口,getNamespace(), getLocalName()方法不总是有效,请注意正确的设置DocumentBuilderFactory的namespaceAware属性


1.3:namespace定义的作用域
默认情况下,
namespace的定义的作用域是当前元素以及所有的子/孙元素。除非子元素使用相同的prefix重新定义了namespace
注意:namespace的定义和引用是两回事情。定义namespace的地方不一定马上使用此namespace

示例1
A元素定义了2个namespace,ns的namespace没有马上使用。而是在子/孙元素中才使用.
< org:A xmlns:org ="http://publish-org" xmlns:ns="http://publication"/>
<ns:Book>
<ns:Name>xxxx</ns:Name>
</ns:Book>
</org:A>

示例2:Book子元素覆盖了A父元素的“ns”的namespace定义。这个示例的xml与前述示例的xml数据完全等价。
<ns:A xmlns:ns="http://publish-org">
<ns:Book xmlns:ns="http://publication">
<ns:Name>xxxx</ns:Name>
</ns:Book>
</ns:A>


1.4:默认的名字空间与未限定名称的XML?
a) 什么是默认的名字空间?
定义namespace时,prefix可以省略,这样的名字空间为默认名字空间。因为此时声明xml元素名时,不需要使用前缀就表示该元素名称受到该名字空间的限定。同样的默认名字空间也可以被子元素继承或者重载
示例1:
<book xmlns=”http://publication”/>
book受默认namespace(http://publication)的限定。

示例2:
<org:A xmlns:org ="http://publish-org" xmlns="http://publication"/>
<book> …</book>
</org:A>
book元素继承了来自父元素的默认名字空间,所以名称受到此默认namespace(http://publication)的限定

b) “茴”字的七种写法——未限定XML的限定书写方式

我们前面提到,在限定名(QName)的技术框架下,可以将非限定名理解为namespace的uri值为空的限定名(QName)。
这意味着我们可以使用声明namespace的方式来书写等价的非限定XML元素。下面给出了3个XML。其中第一个是常规的非限定写法。而后两个则是等价的限定书写方式。

<book>
  <name>xxx</name>
</book>

<ns:book xmlns:ns=””>
  <ns:name>xxx</ns:name>
</ns:book>

<!--请特别注意这个写法与常规的非限定写法的区别-->
<book xmlns=””>
<name>xxx</name>
</book>

我们不想做孔乙己,可是现实很无奈

c) 默认的名字空间与非限定名一起使用时,要特别注意他们的区别。

为了说明这个问题,我们看看以下4个xml。这4个xml数据的前两个等价,后两个等价。

1)
<ns:book xmlns:ns=”http://publication”>
<ns:name>xxx</ns:name>
</ns:book>
book,name元素都受到http://publication名字空间的限定。

2)<book xmlns=”http://publication”>
<name>xxx</name>
</book>
book,name元素都受到http://publication名字空间的限定(默认名字空间的方式)。

3)
<ns:book xmlns:ns=”http://publication”>
<name>xxx</name>
</ns:book>
book受到http://publication名字空间的限定,name元素不受限定。

4)
<book xmlns=”http://publication”>
<name xmlns=””>xxx</name>
</book>
book受到http://publication名字空间的限定,name元素不受限定。
由于book元素定义了默认名字空间,所以name元素必须显示定义uri为空的名字空间,来强制声明自己是不受到namespace限定的。

,是不是比较变态。但这种变态的情况往往会在使用XML Schema技术时出现,下面我们会详细描述。并且很多技术(譬如Webservice引擎)没有正确的处理这种情况,会导致一些微妙的bug

1.5)XML 属性的QName
Xml属性的名称与xml元素的名称一样,也会受到namespace的限定。用于属性的QName的规则与前面描述的xml元素QName的规则基本一致。除了以下1条例外规则:

xml属性的名称必须显示的使用prefix进行限定,不会自动受到默认名字空间的限定。
示例1:
<book xmlns=”http://publication” name=”xxx”/>
虽然定义了默认名字空间(http://publication),但属性"name"不受默认名字空间的限定。

示例2:
<ns:book xmlns:ns=”http://publication” ns:name=”xxx”/>
通过prefix的方式强制指定name属性受到namespace(http://publication)限定

1.6)进一步了解namespace的资料
http://www.w3.org/TR/REC-xml-names/

2)Schema中对namespace使用
2.1)元素名qualified/unqualified的区别
schema中可以使用elementFormDefault来限定xml元素名称为qualified还是unqualified。其作用规则如下:

a)schema中直接定义的xml 元素名不受到qualified/unqualified的影响。总是schema的targetNamespace所定义的namespace的限定。

“直接定义”只是一个形象的说法,我们可以参见下面的示例来区分直接定义的含义。

备注:本文中统一使用xs的prefix指代schema规范的namespace,譬如<xs:schema>表示是一个符合schema规范的schema元素(定义一组xml类型与元素)。

targetNamespace是schema元素的属性,属性值是一个namespace的uri值。主要描述1: schema内直接定义的xml元素(<xs:element>),其名称都受到targetNamespace的指定的namespace的限定。2:schema内定义的类型(<xs:type>)的名称也都受到targetNamespace的指定的namespace的限定



b)schema的xs:type中定义的子元素,其元素名受到qualified/unqualified的影响。
当qualified,表示子元素的名称受到namespace的限定,位于schema的targetNamespace所规定的namespace内。
取值为unqualified,表示子元素的名称不受namespace的限定,即子元素的定义在uri为空的名字空间内。

示例1:qualified示例
<xs:schema xmlns:xs="http://www.w3.org/XML_Schema"
elementFormDefault="qualified" targetNamespace="http://publication">
<xs:element name="book">
<xs:type><xs:sequence>
<xs:element name="name " type="xs:string"/>
</xs:sequence></xs:type>
</xs:element >
</xs:schema>

此schema定义了2个xml元素。其中book子元素是直接元素。name则不是直接元素。
所以book元素不会收到qualified/unqualified的影响。总是会受到schema的targetNamespace的限定。
而name元素则会受到qualified/unqualified影响。在本示例中,因为是qualified,所以name是受到targetNamespace限定的。


符合上述schema定义的合法xml如下:
<ns:book xmlns:ns="http://publication"/>
<ns:name>xxx</ns:name>
</ns:book>

示例2:unqualified

此示例的schema与上述示例的schema基本一样,唯一区别是elementFormDefault属性的值为unqualified。unqualified,所以name不受http://publication的限定;
合法的xml是:
<ns:book xmlns:ns="http://publication"/>
<name >xxx</name > 
</ns:book >


再次提醒:如果实际的xml元素使用了默认名字空间,内部子元素一定要注意qualified和unqualified的区别。必要时,必须在子元素上显示声明uri=””的名字空间,表示xml元素不受到namespace的限定
示例:qualified的另外一种合法写法。
<book xmlns="http://publication"/>
<name>xxx</name>
</book>

示例:unqualified的另外一种合法写法。
<book xmlns="http://publication"/>
<name xmlns=””>xxx</name>
</book>

schema定义的直接XML元素会受到schema的targetNamespace的限定,但不意味着schema直接定义的xml元素的总是namespace受限定的。因为schema可以不定义targetNamespace,此时等价于targetNamespace指定为空。此时schema定义的直接xml元素名受到uri值为空的namespace的限定等同于不限定xml元素名


2.3)元素属性名称qualified与unqualified的区别
schema元素的attributeFormDefault属性可以取值qualified/unqualified。其功能与elementFormDefault类似,只不过用于描述属性名是限定/非限定方式。

通常属性都是使用unqualified,很少特意指定为qualified。我们在xml namespace的基本问题中提到过,除非显示指定,属性名称不会受到默认名字空间的限定。所以这种用法不会象xml元素名那样,因为定义了默认名字空间而导致微妙的错误。

3)XPath中的namespace使用

我们在书写xpath表达式中的元素名时,如果只是简单的书写元素名。xpath总是理解为"这是一个不受namespace限定的元素名";假设我们有以下XML
<book xmlns="http://publication">
<name>xxx</name>
</book>

我们通过名称"book"是根本无法匹配book元素的。因为xpath理解为book是一个不受限定的名称。而实际的xml,book是受到http://publication限定的。

此时我们必须使用prefix:name的书写格式告诉xpath,这是一个受到namespace限定的名称。所以我们需要处理
3.1)如何将前缀与namespace uri的对应关系告诉xpath引擎。
取决与具体使用的XPath引擎。jdk标准的xpath接口,可以参阅
a)javax.xml.xpath.XPath.setNamespaceContext()
b)javax.xml.namespace.NamespaceContext:定义prefix与uri的映射关系。
只需要实现自己的NamespaceContext接口,并调用setNamespaceContext通知xpath引擎即可。
3.2)使用时可以使用我们定义的前缀+ ":"+ 元素名来匹配QName;

假设我们通知xpath引擎, prefix("ns")对应uri("http://publication")。那么我们可以在xpath中使用名称  ns:book来匹配上述xml元素。

4)Webservice的namespace注意问题

4.1)注意qualified/unqualified schema引发的bug
由于webservice基于schema技术实现,因此会遇到qualified/unqualified的schema定义的不同情况。但主要问题在于,很多webservice引擎(尤其是老版本的开源webservice引擎, cxf, axis2, xfire都有类似情况)在处理数据对象到xml转换时,在特殊情况下会使用默认名字空间。此时,容易导致内部子元素错误的受到默认名字空间的限定。从而导致:虽然schema是unqualified,但实际的xml数据却错误的变成了qualified。从而导致webservice调用失败。

对于这种情况,建议找到你所使用webservice引擎处理数据对象的xml映射的规律,总是强迫其不使用默认名字空间。这样会避免错误。譬如CXF的老版本可以在根据wsdl生成java数据对象时,强迫java数据对象类的package与namespace uri不一致。这样可以避免使用默认名字空间

4.2)注意XML namespace继承引发的bug
按照xml namespace的基本规则,父元素定义的namespace会被子元素自动继承。

这样有些ws引擎可能会将xml数据对象用到的namespace定义在soap:Envelope, soap:Body, soap:Header 这些soap协议的标准xml元素上。
譬如:
<soap:Envelope xmlns:ns="http://publication">
<soap:Body>
<ns:Book>
......
</ns:Book>
</soap:Body>
</soap:Envelope>

但是问题在于,虽然这样使用是符合xml namespace规范的,但是由于webservcie引擎在解析soap协议栈时,是分模块的。因此可能会导致在处理用户的xml数据时,无法正确的找到soap 标准xml(envelop, body, header)上声明的xml namespace。从而导致错误发生。

关于这个问题,没有特别好的解决方法,需要注意更新所使用的webservice引擎的实现。这通常会作为bug被较新的版本解决。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics