使用命名空间

前面已经看到,当相同的名字用以实现不同的目的时,必须以某种方式解决slideshow.dtd中定义的title元素和xhtml.dtd中定义的title元素之间的冲突。在前面的练习中,用连字符连接了名字,以便将它放倒不同的“命名空间”。在本节中,会介绍如何不重命名元素就使用XML命名空间标准完成相同功能。

命名空间规范的主要目标是让文档作者告诉解析器解析给定元素时使用哪个DTDSchema。然后,解析器可以查看适当DTD或模式中的元素定义。当然,当出现“复制”的定义的时候,也要确保解析器能够终止,并且如果文档引用了一个元素如title而没有限制它仍然要产生错误(标识DTD或模式用作定义)

注意:命名空间使用于属性和元素。本节中,仅考虑元素。如果想获得关于属性的更多信息,参考http://www.w3.org/TR/REC-xml-names/处的命名空间规范

DTD中定义命名空间

DTD中,通过将属性添加到元素的定义中定义该元素所在的命名空间,其中属性名是 xmlns ("xml namespace")。例如,可以通过在title 元素的属性列表定义中添加下面一项,来在slideshow.dtd 中实现这一目的:

<!ELEMENT title (%inline;)*>

<!ATTLIST title

  xmlns CDATA #FIXED "http://www.example.com/slideshow"

>

将属性定义成FIXED有许多重要特征:

·   它避免了文档为xmlns 属性指定任何不匹配的值。

·   DTD中定义的元素是唯一的 (因为解析器理解xmlns属性),所以它不和另一个DTD中的相同名字的元素冲突。这允许多个DTD使用相同的元素名而不会出错。

·   文档为标签指定xmlns 属性时,文档选择具有匹配属性的元素定义。

说的在详细些,DTD中的每个元素会得到完全相同的属性,并具有相同的值。(但是这里,我们仅仅关心title 元素。)同时也要注意使用CDATA 子符串提供URI。这里将指定一个URL。但是也能通过使用前缀urn: 替代http: 来指定一个URN(当前在研究URN。目前还看不到它们有多大作用,但是将来会改变这个状况。)

引用命名空间

当文档使用一个元素名,而该元素名仅存在于文档引用的一个.DTD或模式中,就不需要限定该名字。但是如果要使用一个多次定义的元素名,就必须做出一些限定。

注意:实际上,元素名通常会受到它的默认命名空间 的限定,由它所在的DTD文件名定义。只要仅有一个地方定义了该名字,限定就是隐含的。


通过指定xmlns 属性限定对元素名的引用,如下所示:

<title xmlns="http://www.example.com/slideshow">

  Overview

</title>

将指定的命名空间应用到该元素以及该元素内的其他元素。

定义命名空间前缀

当仅需要一个命名空间引用时,这就不显得很重要了。但是,如果要多次进行相同的引用,添加xmlns 属性就不太方便了。这也使得以后很难改变命名空间的名字。

另一种方法就是定义命名空间前缀,仅仅需要在属性值前指定xmlns、一个冒号(:)和前缀名,如下所示:

<SL:slideshow xmlns:SL='http:/www.example.com/slideshow'

    ...>

  ...

</SL:slideshow>

该定义建立了SL 作为一个前缀,可以用来指定当前元素名和它之内的任何元素。由于前缀能够用在所有包含元素上,所以最好在XM文档的根元素上定义它,就如这里所示。

注意:命名空间URI可以包含在XML名中无效的字符,所以不能直接将它用作前缀。前缀定义将XML名和URI结合起来,这就允许使用前缀名。以后也能够很容易地将引用变成URI


当使用前缀来限定元素名时,结束标签也包含前缀,如下所示:

<SL:slideshow xmlns:SL='http:/www.example.com/slideshow'

      ...>

  ...

  <slide>

    <SL:title>Overview</SL:title>

  </slide>

  ...

</SL:slideshow>

最后,在同一个元素中可以定义多个前缀,如下所示:

<SL:slideshow xmlns:SL='http:/www.example.com/slideshow'

      xmlns:xhtml='urn:...'>

  ...

</SL:slideshow>

这样安排后,所有定义的前缀都在同一个地方,并且可以在文档中任何需要的地方使用它们。本例也说明了如何使用URN而不是URL来定义xhtml 前缀。该定义允许应用程序引用XHTML DTD的本地副本或一些镜像版本,这能够潜在地提高性能。

使用 XML Schema 验证

既然已经进一步理解了命名空间,下面就深入研究XML Schema验证过程。本节主要介绍使用Schema定义严整XML文档的步骤,虽然对XML Schema的完整的研究超出了本教程的范围(如果想获得关于XML Schema的更多信息,可以查看http://www.w3.org/TR/xmlschema-0/处的在线教程,XML Schema Part 0:入门。也可以查看JAXP下载中的示例程序。它们使用简单的XML Schema定义来验证存储在XML文件中的个人数据。)

注意:现有多种模式-定义语言,包括RELAX NGSchematronW3C "XML Schema"标准。(甚至将DTD限制成"模式",虽然只有它没有使用XML语法来描述模式约束) 然而, "XML Schema"给我们带来了术语上的挑战。虽然"XML Schema 模式"一词更精确,但我们使用词"XML Schema definition"来避免拖沓。

结束本节时,你也将学会如何使用XML Schema定义去验证来包含来自于多个命名空间的元素的文档。

验证过程概述

要想接收到XML文档中验证错误的通知,必须:

1. 配置工厂和合适的错误处理程序集。

2. 将文档跟一个或多个模式联系起来。

配置DocumentBuilder工厂

首先要定义配置工厂时会使用的常量。 (这些常量跟SAX解析中使用XML Schema 时定义的相同)

static final String JAXP_SCHEMA_LANGUAGE =

    "http://java.sun.com/xml/jaxp/properties/schemaLanguage";

 

static final String W3C_XML_SCHEMA =

    "http://www.w3.org/2001/XMLSchema";

接着,必须配置DocumentBuilderFactory 来产生使用XML Schema的理解命名空间的验证解析器:

...

  DocumentBuilderFactory factory =

      DocumentBuilderFactory.newInstance()

  factory.setNamespaceAware(true);

  factory.setValidating(true);

try {

  factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);

}

catch (IllegalArgumentException x) {

  // Happens if the parser does not support JAXP 1.2

  ...

}

由于默认情况下遵守JAXP的解析器不能理解命名空间,因此要使用模式验证必须设置属性。也要设置工厂属性指定使用的解析器语言。(另外,对于SAX解析,设置工厂产生的解析器的属性)

关联文档和模式

既然程序已经做好了验证XML Schema定义的准备,现在只需要确保XML文档和至少一个模式关联。有两种实现方法:

1. 使用XML文档中的模式声明。

2. 指定应用程序中使用的模式。

注意:应用程序指定使用的模式时,它覆盖文档中的任何模式声明。

要在文档中指定模式定义,必须按如下方法创建XML

<documentRoot

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:noNamespaceSchemaLocation='YourSchemaDefinition.xsd'

>

  ...

第一个属性定义XML命名空间(xmlns)前缀“xsi”,其中“xsi”代表“XML Schema 实例”。第二行指定了没有命名空间前缀的文档中元素使用的模式——也就是说,用于那些特别定义的简单文档中的元素。(下一节将介绍如何处理多个命名空间。)

也可以指定应用程序中的模式文件,如下所示:

static final String schemaSource = "YourSchemaDefinition.xsd";

static final String JAXP_SCHEMA_SOURCE =

    "http://java.sun.com/xml/jaxp/properties/schemaSource";

...

DocumentBuilderFactory factory =

    DocumentBuilderFactory.newInstance()

...

factory.setAttribute(JAXP_SCHEMA_SOURCE,

    new File(schemaSource));

同样,你也可以选择一种机制指定多个模式。下面会具体介绍这一点。

验证多个命名空间

通过命名空间可以结合同一文档中的多个用于不同目的的元素结合起来,而不用担心名字重叠。

注意:本节讨论的材料也适用于使用SAX解析器时的验证。你在这里看到这些材料是因为现在你已经对命名空间有了一定的了解,能够理解这些讨论了。

设计一个例子,考虑跟踪个人数据的 XML数据集。数据集包括来自于w2 tax表单的信息,也有来自于雇员雇佣表单的信息,在它们各自的schema中,两个元素都叫做<form>

如果为“tax”命名空间定义了一个前缀,并为“hiring”命名空间定义了另一个前缀,那么个人数据可以包括如下段:

<employee id="...">

  <name>....</name>

  <tax:form>

    ...w2 tax form data...

  </tax:form>

  <hiring:form>

    ...employment history, etc....

  </hiring:form>

</employee>

很明显tax:form 元素的内容可以和hiring:form的内容不同,并且必须进行不同的验证。

也要注意,本例中有一个“默认”的命名空间,没有限制的元素命名它所属的employee name。要正确验证文档,必须声明该命名空间的模式,以及tax hiring 命名空间的模式。


注意:“默认”的命名空间实际上是一个特定的命名空间。它是“没有名字的命名空间”。所以你不能这个星期使用这个命名空间作为默认的命名空间,而下个星期就使用另一个命名空间作为默认的命名空间。这个“未命名的命名空间”或“空命名空间”就像数字中的0一样。它没有任何值,但是仍然要精确定义它。所以不要使用具有名字的命名空间来作为“默认”的命名空间。

解析后,会验证合适的模式上的数据集,只要声明了这些模式。同样,可以将模式声明成XML数据集的一部分或在程序中声明它。(也可以混合声明。总的来说,最好将所有的声明放在同一个地方。)

声明XML数据集中的模式

声明上面的例子中的数据集使用的模式的XML代码看起来如下所示:

<documentRoot

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:noNamespaceSchemaLocation="employeeDatabase.xsd"

  xsi:schemaLocation=

    "http://www.irs.gov/             fullpath/w2TaxForm.xsd

     http://www.ourcompany.com/ relpath/hiringForm.xsd"

  xmlns:tax="http://www.irs.gov/"

  xmlns:hiring="http://www.ourcompany.com/"

>

  ...

前面已经看到了noNamespaceSchemaLocation 声明,是最后两项,定义了命名空间前缀tax hiring 不同的是该项在中间,指定了文档中引用的每个命名空间的模式的位置。

xsi:schemaLocation 声明包含项目对,其中每个条目项中的第一项是完全限定的URI指定了命名空间,第二项包含了模式定义的完整路径或相对路径。(总的说来,推荐使用完全限定的路径。在这种方法中,值要保存模式的一个副本。)

在特定的节点中,定义模式位置时不能使用命名空间前缀。xsi:schemaLocation 声明只能理解命名空间名字,不能理解前缀。

在应用程序中声明模式

为了在应用程序中声明等价的模式,代码必须具有下列形式:

static final String employeeSchema = "employeeDatabase.xsd";

static final String taxSchema                 = "w2TaxForm.xsd";

static final String hiringSchema                 = "hiringForm.xsd";

 

static final String[] schemas = {

    employeeSchema,

    taxSchema,

    hiringSchema,

    };

 

static final String JAXP_SCHEMA_SOURCE =

  "http://java.sun.com/xml/jaxp/properties/schemaSource";

 

...

DocumentBuilderFactory factory =

    DocumentBuilderFactory.newInstance()

...

factory.setAttribute(JAXP_SCHEMA_SOURCE, schemas);

这里,将指向模式定义(.xsd 文件)的字符串数组作为参数传递给factory.setAttribute方法。注意它跟定义将模式作为XML数据集时的区别:

·   没有特定的“默认”(未命名) 模式声明。

·   你不用指定命名空间的名字。相反,只要给出到.xsd 文件的指针。

要分配命名空间,解析器读取.xsd 文件并在其中查找应用的目标命名空间的名字。由于文件是通过URI指定的,解析器可以使用EntityResolver (如果定义了一个)来查找模式的一个本地副本。

如果模式定义没有定义一个目标命名空间,那么就使用“默认”(未命名或空)的命名空间。所以,在前面的例子中,可以在模式中看到这些目标命名空间的声明:

·   employeeDatabase.xsd -- none

·   w2TaxForm.xsd -- http://www.irs.gov/

·   hiringForm.xsd -- http://www.ourcompany.com

这时,调用factory.setAttribute()方法时模式源属性有两个可能值, factory.setAttribute(JAXP_SCHEMA_SOURCE, new File(schemaSource))中的文件对象和factory.setAttribute(JAXP_SCHEMA_SOURCE, schemas)中的字符串数组。下面是参数的可能值的完整的列表:

·   指向模式URI的字符串

·   带有模式内容的InputStream

·   SAX InputSource

·   文件

·   一个对象数组,每个都是上面定义的类型

注意:仅当运行时模式语言( http://java.sun.com/xml/jaxp/properties/schemaLanguage) 能够汇编模式时,才能使用对象数组。同样:传递了对象数组之后,两个模式就不能共享相同的命名空间。

更多信息

要获得关于TreeModel的更多信息,请查看:

·   了解Tree