XML学习笔记(一):XML中的命名空间
由于项目需要对大量的XML文件进行处理,而自己当时的XML知识还仅限在怎么编写XML实例文档,使用简单的XPath对XML文档进行搜索,而对于XML模式、XLST没有任何概念。一句话就是XML的相关知识还是相当的匮乏,所以下决心开始学习XML比较深入的知识,而此次的XML学习之旅将从XML模式开始。并将此学习过程中的一些心得总结出来,以供大家分享。文中有不当之处,望前辈指点一二:)
一、XML中命名空间的作用
之前每当自己编写XML实例文档时总是会被命名空间所困扰,不明白它的意义和作用,但是好像对自己编写XML实例文档也没有什么影响,所以也没有深入的去理解它。其实XML里的命名空间的作用与其它语言(如C#)中命名空间的作用差不多:为命名提供一个容器,只要在该容器中没有相同的命名就可以避免命名冲突。在不同的命名空间里的相同的命名会被解析成两个不同的命名,这样就可以像模块化编程一样编写XML文档(关于XML命名空间的具体作用可以参考W3C描述)。下面的示例中定义了两个XML模式和一个XML实例文档
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="n1schema"
targetNamespace="http://tempuri.org/n1schema.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/n1schema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:complexType name="Address">
<xs:sequence>
<xs:element name="Country" type="xs:string"></xs:element>
<xs:element name="Province" type="xs:string"></xs:element>
<xs:element name="City" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="n2schema"
targetNamespace="http://tempuri.org/n2schema.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/n2schema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:complexType name="Address">
<xs:sequence>
<xs:element name="Country" type="xs:string"></xs:element>
<xs:element name="State" type="xs:string"></xs:element>
<xs:element name="City" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns:n1="http://tempuri.org/n1schema.xsd"
xmlns:n2="http://tempuri.org/n2schema.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<billTo xsi:type="n1:Address">
<n1:Country>China</n1:Country>
<n1:Province>Jiangsu</n1:Province>
<n1:City>Nanjing</n1:City>
</billTo>
<shipTo xsi:type="n2:Address">
<n2:Country>USA</n2:Country>
<n2:State>IL</n2:State>
<n2:City>Chicago</n2:City>
</shipTo>
</root>
在n1shema.xsd和n2schema.xsd中都定义了一个复杂类型Address,然后在multiNamespaceTest.xml实例文档中引入前两个XML模式定义的命名空间,我们可以看到billTo元素定义为n1schema.xsd中定义的Address类型,而shipTo元素定义为n2schema.xsd中定义的Address类型,在multiNamespaceTest.xml中这两个Address类型并不会相互冲突。
二、XML模式创建的命名空间
从上面的n1schema.xsd和n2schema.xsd中可以看出,它们的schema元素都定义了targetNamespace属性(参考W3C描述),该属性就定义了该XML模式所创建的命名空间,所有在该XML模式中声明的元素和定义的类型、属性都处于该命名空间内,这样在引用该命名空间的XML文档就可以引用那些元素和类型、属性了。
n1shema.xsd和n2schema.xsd的根元素xs:schema还可以定义elementFormDefault和attributeFormDefault属性(参考W3C描述),它们的值只能是qualified或unqualified,它们的默认值都是unqualified。elementFormDefault属性用于设定该XML模式创建的命名空间中的局部元素和类型被引用时是否需要指定的前缀;attributeFormDefault属性用于设定该XML模式创建的命名空间中的属性被引用时是否需要指定的前缀。
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="qualifiedSchema"
targetNamespace="http://tempuri.org/qualifiedSchema.xsd"
elementFormDefault="qualified" attributeFormDefault="qualified"
xmlns="http://tempuri.org/qualifiedSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="books" type="BooksType"></xs:element>
<xs:complexType name="BooksType">
<xs:sequence>
<xs:element name="book" type="BookType" minOccurs="1" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BookType">
<xs:sequence>
<xs:element name="ISBN" type="xs:string"></xs:element>
<xs:element name="Name" type="xs:string"></xs:element>
</xs:sequence>
<xs:attribute name="Category" type="xs:string"></xs:attribute>
</xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<q:books xmlns:q="http://tempuri.org/qualifiedSchema.xsd">
<q:book q:Category="Computer">
<q:ISBN>7115134162</q:ISBN>
<q:Name>C++ Primer Plus(第五版)</q:Name>
</q:book>
<q:book q:Category="Computer">
<q:ISBN>7115134162</q:ISBN>
<q:Name>C++ Primer Plus(第五版)</q:Name>
</q:book>
</q:books>
由上可以看到qualifiedTest.xml中引用qualifiedSchema.xsd中定义的属性和元素时都加上了q:前缀。而下面的两段代码显示了elementFormDefault和attributeFormDefault属性设为unqualified的情形
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="unqualifiedSchema"
targetNamespace="http://tempuri.org/unqualifiedSchema.xsd"
xmlns="http://tempuri.org/unqualifiedSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="books" type="BooksType"></xs:element>
<xs:complexType name="BooksType">
<xs:sequence>
<xs:element name="book" type="BookType" minOccurs="1" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BookType">
<xs:sequence>
<xs:element name="ISBN" type="xs:string"></xs:element>
<xs:element name="Name" type="xs:string"></xs:element>
</xs:sequence>
<xs:attribute name="Category" type="xs:string"></xs:attribute>
</xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<q:books xmlns:q="http://tempuri.org/unqualifiedSchema.xsd">
<book Category="Computer">
<ISBN>7115134162</ISBN>
<Name>C++ Primer Plus(第五版)</Name>
</book>
<book Category="Computer">
<ISBN>7115134162</ISBN>
<Name>C++ Primer Plus(第五版)</Name>
</book>
</q:books>
但是,无论 elementFormDefault和attributeFormDefault属性设为qualified还是unqualified,引用XML模式中定义的全局元素都必须加上q:前缀(有种情况下,引用XML模式中定义的全局元素不需要加上前缀,该情况会在第三部分进行介绍)。
当然,这里还有一种特殊的情况:XML模式的schema元素没有定义targetNamespace属性,那么该XML模式就不会创建命名空间。
三、XML模式的引用方式
引用具有命名空间的XML模式
我们在编写XML实例文档时,可以引用本地XML模式创建的命名空间,也可以引用网络中可访问的XML模式定义的命名空间,但是它们的引用方式不一样。前面两部分的xml文件都是引用本地XML模式定义的命名空间。下面介绍一下如何引用网络中可访问的XML模式创建的命名空间。
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd">
<books>
<book>
<Name xsi:type="xs:string">C#入门到精通</Name>
<Price xsi:type="xs:decimal">25.6</Price>
</book>
</books>
</root>
引用XML模式创建的命名空间,需要在XML实例文档的根元素定义xmlns:xxx属性,该属性值即为XML模式创建的命名空间,通过定义xmlns:xxx属性将xxx与XML模式创建的命名空间绑定起来,之后引用XML模式中定义的元素、属性和类型时加上xxx:前缀即可(如果XML模式的schema元素的elementFormDefault与attributeFormDefault为qualified)。如果引用的命名空间不是像http://www.w3.org/2001/XMLSchema这样通用的话,还必须指明创建该命名空间的XML模式文件的URI,这就是xsi:schemaLocation属性所做的事。xsi:schemaLocation属性值为“命名空间 XML模式文件的URI”的列表,各对之间使用空格相隔。上例中的xsi:schemaLocation属性值的意思是:创建http://www.w3.org/2001/XMLSchema命名空间的XML模式文档的URI为http://www.w3.org/2001/XMLSchema.xsd。
在第2部分提到:有种情况下,引用XML模式中定义的全局元素也不需要加上前缀。这是通过设定XML实例文档的默认命名空间来实现的。
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="defaultSchema"
targetNamespace="http://tempuri.org/defaultSchema.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/defaultSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="root" type="RootType"></xs:element>
<xs:complexType name="RootType">
<xs:sequence>
<xs:element name="Country" type="xs:string"></xs:element>
<xs:element name="City" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns="http://tempuri.org/defaultSchema.xsd">
<Country>China</Country>
<City>Nanjing</City>
</root>
我们可以看到,在defaultNamespaceTest.xml中设定了根元素的xmlns属性,将该XML实例文档的默认命名空间设定为http://tempuri.org/defaultSchema.xsd,这样,在XML实例文档中引用defaultSchema.xsd中定义的全局元素root时,也可以不用加上特定的前缀。
引用没有命名空间的XML模式
在第2部分我们介绍了没有定义targetNamespace属性的XML模式是不会创建命名空间的,那么我们在编写XML实例文档时怎么引用无命名空间的XML模式中定义的属性、元素和类型呢?
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="noNamespace"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="root" type="USAddress"></xs:element>
<xs:complexType name="USAddress">
<xs:sequence>
<xs:element name="Country" type="xs:string"></xs:element>
<xs:element name="City" type="xs:string"></xs:element>
<xs:element name="District" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="D:\VS2010 study\XmlStudy\XmlTest\noNamespace.xsd">
<Country></Country>
<City></City>
<District></District>
</root>
上面的例子演示的是引用本地无命名空间的XML模式的方式,就是要设定xsi:noNamespaceSchemaLocation属性,它的值为本地XML模式文档所在的路径(可以是绝对路径,也可以是相对路径)。引用网络中无命名空间的XML模式的方式与引用本地无命名空间的XML模式是一样的,只需要将xsi:noNamespaceSchemaLocation属性的值设为网络中XML模式文档的URI。