经典技术文章转载:在 XML 架构中使用命名空间

来源:互联网 发布:手机怎样恢复不了数据 编辑:程序博客网 时间:2024/05/16 11:07
Posted on 2010-02-06 20:56 Frank Xu Lei 阅读(6) 评论(0)  编辑 收藏 网摘 所属分类: 经典技术文章转载
在 XML 架构中使用命名空间
发布日期 : 4/1/2004 | 更新日期 : 4/1/2004

Dare Obasanjo

Microsoft Corporation

2002 年 8 月 20 日

本页内容


    强大工具的王国
   强大工具的王国

    目标命名空间与架构位置:有何区别? 
   目标命名空间与架构位置:有何区别?

    如果首战失利
   如果首战失利

    放眼全局,立足本地
   放眼全局,立足本地

    您的命名空间是否受到限定?
   您的命名空间是否受到限定?

    事物的整体比它各个部分的总和大
   事物的整体比它各个部分的总和大

    Karma Chameleon
   Karma Chameleon

    更多信息
   更多信息

摘要:Dare Obasanjo 讨论了 W3C XML 架构的各个方面以及命名空间对它们的影响。涉及的主题包括 targetNamespace、elementFormDefault 和 attributeFormDefault 属性以及 include、import 和 redefine 元素在架构中的正确用法。(13 页打印页)

强大工具的王国

本周末,我曾提到的在上一篇文章中订购的书架终于到货了。我没有出去购买工具,而是急切地尝试只用螺丝刀和旧鞋(用作锤子)将它组合在一起。在手上起了几个泡且花费了几个小时后,我的书架组装完毕,但是稍微有点摇晃。

我打电话告诉妻子,我只是“将某些家具组合在一起”就起了几个泡时,她禁不住笑了,于是我决定继续使用基于 XML 的图书目录,以试图恢复我的尊严。

我决定为 XML 实例创建一个架构,以便我不仅可以在构建的应用程序中检查它们的有效性,还可以根据需要使用 .NET XML 序列化中的绝佳功能,将 XML 转换为 C# 对象。

但首先,我需要一个方便的命令行工具来执行实例文档和架构的验证。下面是我构建的用于简化此过程的工具:

using System; using System.Xml; using System.Xml.Schema;public class XsdValidate{  static XmlSchemaCollection sc = new XmlSchemaCollection();  static string xsdFile = null;   static string xmlFile = null;   static string nsUri = null;   static string usage = @"Usage: xsdvalidate.exe [-xml <xml-file>]    [-xsd <schema-file>] [-ns <namespace-uri>]Sample:  xsdvalidate.exe -xml t.xmlValidate the XML file by loading it into XmlValidatingReader with   ValidationType set to auto.  Sample:  xsdvalidate.exe -xml t.xml -xsd t.xsd -ns ns1This will validate the t.xml with the schema t.xsd with target namespace 'ns1'Sample:  xsdvalidate.exe xsd t.xsd -ns ns1This will validate the schema t.xsd with target namespace 'ns1'";  public static void ValidationCallback(object sender, ValidationEventArgs args) {    if(args.Severity == XmlSeverityType.Warning)      Console.Write("WARNING: ");    else if(args.Severity == XmlSeverityType.Error)      Console.Write("ERROR: ");    Console.WriteLine(args.Message); // Print the error to the screen.  }  public static void Main(string[] args){    if((args.Length == 0) || (args.Length %2 != 0)){      Console.WriteLine(usage);      return;     }     for(int i = 0; i < args.Length; i++) {       switch(args[i]){       case "-xsd":     xsdFile = args[++i];     break;        case "-xml":     xmlFile = args[++i];     break;            case "-ns":    nsUri  = args[++i];     break;        default:     Console.WriteLine("ERROR: Unexpected argument " + args[i]);    return;        }//switch     }//for     if(xsdFile != null){              sc.ValidationEventHandler += new ValidationEventHandler(ValidationCallback);       sc.Add( nsUri, xsdFile);       Console.WriteLine("Schema Validation Completed");     }      if(xmlFile != null){       XmlValidatingReader vr = new XmlValidatingReader(new XmlTextReader(xmlFile));       vr.Schemas.Add(sc);        vr.ValidationType = ValidationType.Schema;       vr.ValidationEventHandler += new ValidationEventHandler(ValidationCallback);       while(vr.Read());       Console.WriteLine("Instance Validation Completed");     }  }//Main}//XsdValidate
返回页首

目标命名空间与架构位置:有何区别?

我必须首先决定是否要使用目标命名空间创建架构。架构的目标命名空间指定可由该架构验证的元素和属性的命名空间。由于我上一篇文章中的实例文档使用了

urn:xmlns:25hoursaday-com:my-bookshelf
命名空间,因此实际上是选择要将该命名空间用作目标命名空间,还是在没有命名空间的情况下创建实例文档。

假设我有效地创建了一个新的标记词汇表,并且命名空间提供了一个用来消除标记词汇表歧义的机制,我决定使用目标命名空间。这样,架构中的全局(或顶层)元素和属性声明将只引用

urn:xmlns:25hoursaday-com:my-bookshelf 
命名空间的元素和属性。这同样适用于架构中的全局类型定义。下面显示的是我架构的第一行:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf"xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf">

我作出的第二个相关决定是在 XML 实例文档中使用架构位置。在实例文档中使用

http://www.w3.org/2001/XMLSchema-instance
命名空间的
schemaLocation
noNamespaceSchemaLocation
属性,可以提供对一个或多个可用于验证文档的架构的硬编码引用。引用的架构适用于整个文档,而不仅仅是出现这些架构的元素作用域。但是,在第一次出现其命名空间名称与架构的目标命名空间相同的属性或元素之后,再指定架构位置是错误的。

 

schemaLocation 
属性的值是一对或更多对目标命名空间和架构位置的 URI 引用。下面是一个实例文档的代码片断,该片断使用
schemaLocation
属性来引用要用于验证该文档的架构的目标命名空间和位置:
<bk:books xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf" xsi:schemaLocation="urn:xmlns:25hoursaday-com:my-bookshelf file:///C:/books.xsd" >

 

noNamespaceSchemaLocation 
的值是对没有目标命名空间的架构的单个 URI 引用。

schemaLocation
noNamespaceSchemaLocation
属性只是验证处理器的提示,如果您使用其他方法来为文档指定架构,则可以忽略这些提示。

我希望在不同的计算机(可能有也可能没有 Internet 连接)上使用我的实例文档,因此决定在这些文档中使用

schemaLocation
noNamespaceSchemaLocation
属性,这样对架构的硬编码引用在许多情况下将不适用。
返回页首

如果首战失利

在重新考虑我的 XML 图书目录的格式后,我决定将

on-loan 
属性从根元素中移除,但保持其余的格式不变。这样,假设使用上一篇文章中的以下实例文档(已稍加修改):
<?xml version="1.0" encoding="UTF-8" ?> <bk:books xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf"><bk:book publisher="IDG books" on-loan="Sanjay" ><bk:title>XML Bible</bk:title> <bk:author>Elliotte Rusty Harold</bk:author></bk:book><bk:book publisher="QUE"><bk:title>XML By Example</bk:title> <bk:author>Benoit Marchal</bk:author></bk:book></bk:books>

我创建了以下架构以验证该文档及其相关内容:

<?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"     targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf"    xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf"> <xs:element name="books">   <xs:complexType>   <xs:sequence>     <xs:element name="book" type="bk:bookType" maxOccurs="unbounded" />   </xs:sequence>   </xs:complexType> </xs:element> <xs:complexType name="bookType">  <xs:sequence>   <xs:element name="title" type="xs:string" />   <xs:element name="author" type="xs:string" />  </xs:sequence>  <xs:attribute name="publisher" type="xs:string" />  <xs:attribute name="on-loan" type="xs:string" use="optional" /> </xs:complexType></xs:schema>

令人惊讶的是,尽管上面的架构已使用我的验证工具成功验证,但是在我尝试验证该 XML 实例文档时,还是显示了多条错误信息,特别是在我执行以下命令时:

xsdvalidate -xml books.xml -xsd books.xsd -ns urn:xmlns:25hoursaday-com:my-bookshelf

我得到了以下输出结果(去掉了行号):

Schema Validation CompletedERROR: Element 'urn:xmlns:25hoursaday-com:my-bookshelf:books' has invalid childelement 'urn:xmlns:25hoursaday-com:my-bookshelf:book'. Expected 'book'ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:book' element is not declared. WARNING: Could not find schema information for the attribute 'publisher'. WARNING: Could not find schema information for the attribute 'on-loan'. ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:title' element is not declared. ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:author' element is not declared. ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:book' element is not declared. WARNING: Could not find schema information for the attribute 'publisher'. ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:title' element is not declared. ERROR: The 'urn:xmlns:25hoursaday-com:my-bookshelf:author' element is not declared. Instance Validation Completed

第一条错误信息提示我出现了什么错误。我迅速将架构的第一行更改为:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"     targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf"    xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf" elementFormDefault="qualified" >

当我重新运行工具后,得到以下输出结果:

Schema Validation CompletedInstance Validation Completed

之所以出现错误信息,是因为该架构中包含局部元素声明,并且

xs:schema
元素的
elementFormDefault
属性的默认值为“unqualified”。这些概念将在后面的小节中详细说明。
返回页首

放眼全局,立足本地

作为

xs:schema
元素子级出现的元素和属性声明被视为全局声明。所有其他的元素和属性声明都被视为局部声明。局部元素或属性声明可通过
ref
属性来引用全局声明,这可有效地使局部声明与全局声明保持一致。全局声明的名称与局部声明的名称放在不同的符号空间中。此外,局部声明名称的作用域是其封闭类型定义的作用域。因此,一个架构可以包含两个或多个包含同名元素或属性声明的类型定义,且不会发生命名冲突。这对于与一个或多个局部元素或属性共享同一个名称的全局元素或属性同样适用。

局部元素声明和对全局元素的引用都可以使用出现约束来表达它们的基数。出现约束是使用

minOccurs
maxOccurs
属性指定的。

下面是使用局部元素和全局元素,以及对全局元素声明引用的示例架构:

<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"  targetNamespace="http://www.example.com" xmlns="http://www.example.com"> <!-- global element declaration -->  <xs:element name="language" type="xs:string" /> <!-- complex type with local element declaration -->  <xs:complexType name="sequenceOfLanguages" >    <xs:sequence>   <xs:element name="language" type="xs:NMTOKEN" maxOccurs="unbounded" />  </xs:sequence> </xs:complexType> <!-- complex type with reference to global element declaration -->  <xs:complexType name="sequenceOfLanguages2" >    <xs:sequence>   <xs:element ref="language" maxOccurs="10" />  </xs:sequence> </xs:complexType></xs:schema>

默认情况下,全局元素的命名空间名称与架构的目标命名空间名称相同,而局部元素没有命名空间名称。这意味着,对于上面的架构,全局

language
元素声明可以验证实例文档中命名空间名称为
http://www.example.com
language
元素。但是,
sequenceOfLanguages
类型中
language
元素的局部声明只能验证实例文档中没有命名空间名称的
language
元素。

作为

xs:schema
元素子级出现的类型定义被视为全局类型定义。全局类型定义必须具有名称。全局类型定义可以通过属性和元素声明的
type
属性或派生类型的
base
属性来引用。类型定义还可以在本地作为元素或属性声明的一部分来创建,在这种情况下,它们不能具有名称且被视为匿名类型

下面是使用匿名类型定义和全局类型定义,以及对全局类型定义引用的示例架构:

<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"  targetNamespace="http://www.example.org"  xmlns:tns="http://www.example.org"> <!-- element declaration that references a global complex type -->  <xs:element name="languages" type="tns:sequenceOfLanguages" /> <!-- global complex type definition --> <xs:complexType name="sequenceOfLanguages" >    <xs:sequence>   <xs:element name="language" type="xs:NMTOKEN" maxOccurs="unbounded" />  </xs:sequence> </xs:complexType> <!-- attribute declaration with anonymous simple type  --> <xs:attribute name="positiveDecimal">  <xs:simpleType>    <xs:restriction base="xs:decimal">   <xs:minExclusive value="0"  />  </xs:restriction> </xs:simpleType> </xs:attribute></xs:schema>

类型定义、元素声明和属性声明不共享同一个名称符号空间。因此,在一个架构中,可以允许类型定义、全局声明和局部声明共享同一个名称。这种做法很容易产生混淆,应当避免。

返回页首

您的命名空间是否受到限定?

在上一节中,我曾提到在默认情况下,全局声明用于验证具有命名空间名称的元素或属性,而局部声明用于验证不具有命名空间名称的元素或属性。用来描述具有命名空间名称的元素或属性的术语是“命名空间限定”。

根据局部声明是否验证命名空间限定的元素和属性,默认方式可能会被重写。

xs:schema
元素具有
elementFormDefault
attributeFormDefault
属性,分别用来指定架构中的局部声明是否应该验证命名空间限定的元素或属性。这两个属性的有效值是 qualifiedunqualified,默认值都是 unqualified

局部元素和属性声明的 form 属性可用于重写

xs:schema
元素的
elementFormDefault
attributeFormDefault
属性值。这样就可以更精确地控制相对于局部声明如何验证实例文档中的元素和属性。

以下示例强调用户如何使用

elementFormDefault
attributeFormDefault
 form
属性来控制局部声明。

架构

有效的实例文档

 

<?xml version="1.0" encoding="UTF-8" ?>

 

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 

 

targetNamespace="http://www.example.org" 

 

xmlns:tns="http://www.example.org">

 

<!-- elementFormDefault and attributeFormDefault 

 

have value unqualified in this schema     --> 

 

<xs:element name="root" type="tns:rootType" />

 

<xs:complexType name="rootType" >  

 

<xs:sequence>

 

<xs:element name="child1" type="xs:string" maxOccurs="2" />

 

<xs:element name="child2" type="xs:string" form="qualified" />

 

</xs:sequence>

 

<xs:attribute name="attr" type="xs:string" />

 

</xs:complexType>

 

</xs:schema>

 

<?xml version="1.0" encoding="UTF-8" ?>

 

<ex:root xmlns:ex=http://www.example.org attr="unqualified">

 

<child1>I am not namespace qualified</child1>

 

<child1>Neither am I</child1>

 

<ex:child2>I am namespace qualified</ex:child2>

 

</ex:root>

 

<?xml version="1.0" encoding="UTF-8" ?>

 

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 

 

targetNamespace="http://www.example.org" 

 

xmlns:tns="http://www.example.org" 

 

elementFormDefault="qualified" 

 

attributeFormDefault="qualified">

 

<xs:element name="root" type="tns:rootType" />

 

<xs:complexType name="rootType" >  

 

<xs:sequence>

 

<xs:element name="child1" type="xs:string" maxOccurs="2" />

 

<xs:element name="child2" type="xs:string"  />

 

</xs:sequence>

 

<xs:attribute name="attr1" type="xs:string" />

 

<xs:attribute name="attr2" type="xs:string" form="unqualified"/>

 

</xs:complexType>

 

</xs:schema>

 

<?xml version="1.0" encoding="UTF-8" ?>

 

<ex:root xmlns:ex="http://www.example.org"

 

ex:attr1="qualified" attr2="unqualified">

 

<ex:child1>I am namespace qualified</ex:child1>

 

<ex:child1>So am I</ex:child1>

 

<ex:child2>Me too</ex:child2>

 

</ex:root>
返回页首

事物的整体比它各个部分的总和大

在验证过程中,可以通过组合成单个逻辑架构的多个架构来创建一个架构。W3C XML 架构提供三个可用于将外部架构的全局声明和类型定义组合成一个目标架构文档的元素。这三个元素是

xs:include
xs:import
xs:redefine

 

xs:include
用于引入某些架构的定义,这些架构要么没有目标命名空间,要么与封闭架构具有相同的目标命名空间。
xs:import
类似于 xs:include,但区别在于导入的架构必须与封闭架构具有不同的目标命名空间。如果导入的架构没有命名空间名称,则封闭架构必须有一个目标命名空间。

下面的示例显示如何在封闭架构中引用导入的声明,以及命名空间限定如何影响局部声明。

<?xml version="1.0" encoding="UTF-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"  targetNamespace="http://www.example.org"  xmlns:tns="http://www.example.org"xmlns:imp="http://www.import.org"><xs:import namespace="http://www.import.org" schemaLocation="file:///c:/import.xsd"  <xs:element name="root" type="imp:rootType" /></xs:schema>Imported Schema: import.xsd<xs:schema xmlns:xs=http://www.w3.org/2001/XMLSchematargetNamespace="http://www.import.org" elementFormDefault="qualified"> <xs:complexType name="rootType" >    <xs:sequence>   <xs:element name="child1" type="xs:string" maxOccurs="2" />   <xs:element name="child2" type="xs:string"/>  </xs:sequence></xs:complexType></xs:schema>Instance Document: [when elementFormDefault="qualified" in import.xsd]<?xml version="1.0" encoding="UTF-8" ?><ex:root xmlns:ex=http://www.example.org xmlns:imp="http://www.import.xsd"> <imp:child1>I am from imported schema </imp:child1> <imp:child1>So Am I </imp:child1> <imp:child2>Me too </imp:child2></ex:root>Instance Document: [when elementFormDefault="unqualified" in import.xsd]<?xml version="1.0" encoding="UTF-8" ?><ex:root xmlns:ex="http://www.example.org"> <child1>Don't know where I come from </child1> <child1>neither do I </child1> <child2>Me too </child2></ex:root>

xs

:redefine
通过执行两项基本任务来重定义类型。首先,作为
xs:include
,从另一个架构文档引入声明和定义,并使它们成为当前目标命名空间的一部分。包含的声明和类型必须来自具有相同目标命名空间或无命名空间的架构。其次,可以使用与用新定义替换旧定义的类型派生相同的方式来重定义类型。

W3C XML Schema Primer 中包括示例以及对 xs:includexs:importxs:redefine 的进一步说明。

返回页首

Karma Chameleon

没有目标命名空间的架构通常称为 Chameleon 架构。Chameleon 架构可以包括在具有任意目标命名空间的架构中,这可让 Chameleon 架构中的类型定义和声明获取封闭架构的目标命名空间。

这里有一个使用 chameleon 架构的示例。

返回页首

更多信息

  • W3C XML Schema Primer

  • XML Schemas:Best Practices

  • W3C XML Schema Design Patterns:Dealing With Change

Dare Obasanjo 是 Microsoft 的 WebData 小组的成员,该小组在 .NET 框架的 System.Xml 和 System.Data 命名空间、Microsoft XML 核心服务 (MSXML) 和 Microsoft 数据访问组件 (MDAC) 中开发组件。

有关本文的任何问题或评论,欢迎张贴到 GotDotNet 上的 Extreme XML 留言板

原文地址:http://msdn.microsoft.com/zh-cn/library/ms950796.aspx