第9章 实体和外部DTD子集

80酷酷网    80kuku.com

  

XML教程

 

一个简单的XML文档从许多不同的资源和文件中取得数据和声明。实际上,有些数据直接来自数据库、CGI脚本或其他非文件格式资源。无论采取何种形式,保存XML文档片段的项目称为实体。实体引用把实体载入到XML主文档中。通用实体引用载入数据到XML文档的基本元素中,而参数实体引用载入数据到文档的DTD中。

本章的主要内容如下:

* 什么是实体?

* 内部通用实体

* 外部通用实体

* 内部参数实体

* 外部参数实体

* 怎样从局部开始创建文档

* 结构完整文档中的实体和DTD

9.1 什么是实体?

从逻辑上说,一个XML文档由一个序进程构成,序进程后有一严密地包含了所有其他元素的基本元素。但XML文档的实际数据可以扩展分布在若干文档中。例如,即使一个棒球联盟中包含了大约900个的所有球员,每个PLAYER元素也可以以独立的文件形式存在。包含XML文档细节内容的存储单元称为实体(entities),实体可能是由一个文件、一个数据库记录或其他包含数据的项目组成。例如,本书中所有完整的XML文件都是实体。

包含XML声明或文档类型声明的存储单元和基本元素称为文档实体(document entity)。不过基本元素和它的派生元素也可包含指向即将插入文档的附加数据的实体引用。进行正确性检查的XML处理器在提交文档给最终应用程序以前或显示文件以前,将把所有不同的实体引用结合为单一逻辑结构的文档。

不进行正确性检查的处理器可以但不一定插入外部对象;他们必须插入内部对象。

实体的主要目的在于保存如下内容:结构完整的XML,其他形式的文本或二进制数据。序进程和文档类型声明是它们所属文档的基本元素的一部分。仅当XSL样式单本身就是一个结构完整的XML文档时,才能作为一个实体。组成XSL样式单的实体并不是应用该样式单的XML文档的组成实体之一。CSS样式单根本就不是一个实体。

大多数实体具有一个可以引用的名。唯一的例外是包含XML文档的主文件与文档实体(与数据库记录、CGI程序的输出结果或其他数据相对比,文档实体也不一定是文件)。文档实体无论采取何种结构,都是一种存储单元,用于储存XML声明、文档类型声明(如果有)和基本元素。因此每个XML文档至少拥有一个实体。

有两种类型的实体:内部实体和外部实体。完全在文档实体内部定义的实体称为内部实体。文档本身就是这样的实体,所以所有的XML文档至少有一个内部实体。

相反,经由URL定位的资源中获取的数据称为外部实体。主文档仅包含一个实际引用数据位置的URL。在HTML中,包含于<HTML></HTML>标记之间的文档本身是内部实体时,而IMG元素(实际的图像数据)代表外部实体。

实体分为两类:可析和不可析实体。可析实体包含结构完整的XML文本。不可析实体包含二进制数据或非XML文本(类似电子邮件信息)。如果从本质上说,当前大多数XML处理器不能很好地支持(如果不是完全支持的话)不可析实体,本章所关注的是可析实体。

11章,非XML数据和不可析对象的嵌套。

通过实体引用,可把来源于多个实体的数据合并在一起构成一个单一的文档。通用实体引用把数据并入到文档内容中。参数实体引用把声明并入到文档的DTD中。实中<;、>;、&apos;、"e;、&;是预定义的体引用,分别指的是文本实体<>&符号。然而也可在文档DTD中定义新的实体。

.2 内部通用实体

内部通用实体引用可看作经常使用的文本或强制格式文本的缩写。DTD中的<!ENTITY>标记定义缩写,并且该缩写就代替了文本。例如,可在DTD中简单地把页脚定义为实体footer,然后每页只需键入&footer;,而勿需在每页底部键入相同的页脚。此外,若决定更改页脚块(也许是因为你的电子邮件地址更改了),就仅需在DTD中作一次更改即可,勿需对共享同一页脚的页面逐个进行更改。

通用实体引用以“&”符号开始,以结尾,两个符号之间为实体名。例如,“<"就是小于符号(<)的通用实体引用,实体名为lt,该实体的替换文本就是一个字符"<”。实体名由字母和数字的混合排列以及下划线构成,禁止使用空格和其他标点符号字符。类似XML中的其他内容,实体引用是区分大小写的。

尽管从技术上说,允许在对象名中使用冒号,但正如第18章中所提及,此符号要保留用于命名域(namespace)。

9.2.1 定义内部通用实体引用

DTD中使用标记<!ENTITY>标记定义内部通用实体引用,具有如下格式:

<!ENITY name "replacement text">

namereplacement text的缩写。替换文本需放置于双引号中,因为其中可能包含空格和XML标记。可在文档中键入实体名,而读者所见为替换文本。

例如,我的名字为“Elliotte Rusty Harold"(这得怪我父母取了一个长名)。即使经过多年习惯,我依然常常打错。我可以为我的名字定义通用实体引用,这样每次当我键入&ERH;时,读者将会看见"Elliotte Rusty Harold",这个定义如下:

<!ENITY ERH " Elliotte Rusty Harold">

清单9-1示例说明了&ERH;通用实体引用,图9-1中显示了载入到Internet Explorer的文档。可看出,源代码中的&ERH;实体引用输出时被替换为"Elliotte Rusty Harold"

9-1 清单9-1在内部通用实体引用被实际实体替换后的情形

清单9-1ERH内部通用实体引用

<?xml version="1.0" standalone="Yes">

<!DOCTYPE DOCUME [

<!ETITY ERH "Elliotte Rusty Harold">

<!ELEMENT DOCUME (TITLE, SIGNATURE)>

<!ELEMENT TITLE (#PCDA A)>

<!ELEMENT COPYRIGHT (#PCDATA)>

<!ELEMENT EMAIL (#PCDA A)>

<!ELEMENT LAST_MODIFIED (#PCDATA)>

<!ELEMENT SIGNATURE (COPYRIGHT, EMAIL, LAST_MODIFIED)>

]>

<DOCUMENT>

<TITLE>&ERH;</TITLE>

<SIGNATURE>

<COPYRIGHT >1999 &ERH;</COPYRIGHT>

<EMAIL>elharometalab.unc.edu</EMAIL>

<LAS _MODIFIED>March 10, 1999</LAS _MODIFIED>

</SIGNATURE>

</DOCUMENT>

注意其中的通用实体引用,即使COPYRIGHTTITLE声明为仅接受#PCDATA的子类,&ERH;依然出现在它们之中。因&ERH; 实体引用的替换文本是可析的字符数据,所以这种排列是合法的。所有的实体引用被实体值替换后,才对文档进行正确性检查。

在使用样式单时,也会发生相同的情形。当存在样式单引用,在实体引用替换为实体值后,才把样式应用于替换后实际存在的元素树状结构中。

可按下列方式把版权、电子邮件或最后的修改日期声明为通用实体引用:

<!ENTITY COPY99 "Copyright 1999">

<!ENTITY EMAIL "elharometalab.unc.edu">

<!ENTITY LM "Last modified: ">

因日期对不同的文档可能会发生改变,故而忽略了&LM;中的日期。若把日期作为一个实体引用,不会带来任何好处。

现在,就可把清单9-1内容重写成更加简洁的形式:

<DOCUMENT>

<TITLE>&ERH;</TITLE>

<SIGNATURE>

<COPYRIGHT>©99; &ERH;</COPYRIGHT>

<EMAIL>&EMAIL;</EMAIL>

<LAS _MODIFIED>&LM; March 10, 1999</LAST_MODIFIED>

</SIGNATURE>

</DOCUMENT>

应用实体引用代替书写文本全文的一个好处是使得更改文本更加简便,在简单的DTD被若干文档共享的情况下,特别有用。例如,假设把电子邮件地址从elharometalab.unc.edu更改为eharoldsolar.stanford.edu,仅需按如下内容更改DTD中的一行内容,而勿需在多个文档中寻找和替换邮件地址:

<!ENTITY EMAIL "eharoldsolar.stanford.edu">

9.2.2 DTD中使用通用实体引用

读者或许对是否能像下面的代码一样在一个通用实体引用中包含另一个通用实体引用表示怀疑,如下所示:

<!ENTITY COPY99 "copyright 1999 &ERH;">

实际上该例是合法的,因为ERH实体作为COPY99实体的一部分存在,而COPY99实体本身最终又成为文档内容的一部分。尽管存在某些限制,对于DTD中的其他地方,若最终能转换成文档内容的一部分(例如作为缺省属性值),则也可在此处使用通用实体引用。第一条限制:语句中不能使用如下的循环引用:

<!ENTITY ERH "©99 Elliotte Rusty Harold">?

<!ENTITY COPY99 "copyright 1999 &ERH;">?

第二条限制:通用实体引用不能插入仅为DTD的一部分且不能作为文档内容的文本。例如,下述简略用法的企图无法实现:

<!ENTITY PCD "(#PCDATA)">

<!ELEMENT ANTIMAL &PCD;>

<!ELEMENT FOOD &PCD;>

然而,利用实体引用把文本合并到文档的DTD中的方法常常是有用的。为此目的,XML使用将在下章中讲述的参数实体引用来实现这一目的。

对通用实体值的限制仅在于不能直接包含三种字符:% &,可是能经过使用字符引用包含这三种字符。若&%仅作为实体引用的开头,而不代表自身含义,则可包含其中。限制很少意味着实体可包含标记和分割为多行。例如下面的SIGNATURE实体是有效的:

"<SIGNATURE>

<COPYRIGHT>1999 Elliotte Rusty Harold</COPYRIGH >

<EMAIL>elharometalab.unc.edu</EMAIL>

<LAST_MODIFIED>March 10, 1999</LAST_MODIFIED>

</SIGNATURE>"

>

下一个关心的问题是实体是否可以拥有参数。能否使用上面的SIGNATURE实体,但却改变每页中每一独立的LAST_MODIFIED元素的数据?答案是否定的;实体仅为静态的替换文本。若需要给实体传送数据,应改为使用标记符,并在样式单中随同提供适当的实现指令。

9.2.3 预定义通用实体引用

XML预定义五个通用实体引用,如表9-1所示。五个实体引用出现在XML文档中用来代替一些特殊的字符,这些字符如果不用引用方式就会被解释为标记。例如实体引用<;代表小于号<,小于符号<可解释为标记的开头。

考虑到最大限度的兼容,若计划使用预定义实体引用,就该在DTD中声明这些引用。因为需要避免DTD中字符的递归引用,所以声明时必须相当小心。为方便引用的声明,字符引用使用字符的十六进制ASCII值。清单9-2显示了所需要的声明。

9-1 XML中的预定义实体引用

实体引用

字符

&

&

<

<

>

>

"

"

'

清单9-2:预定义通用实体引用声明

<!ENTITY lt "&#60;">

<!ENTITY gt ">">

<!ENTITY amp "&#38;">

<!ENTITY apos "'">

<!ENTITY quot """>

9.3 外部通用实体

包含基本元素/文档实体的主文件以外的数据称为外部实体。通过外部实体引用可在文档中嵌入外部实体和从若干相互独立的文件中获取数据组建为XML文档。

仅使用内部实体的文档非常类似于HTML模式。文档的完整文本存放于单一文件中,图像、JAVA小程序、声音和非HTML数据也可链接入文件中,但至少在文件中要有所有的文本。当然,HTML模式存在一些问题。特别在文档中嵌入动态信息的过程是一件非常困难的事情。可通过使用CGIJAVA小程序所爱好的数据库软件、服务器方面提供的手段和其他各种各样的方法嵌入动态信息,但HTML仅提供静态文档支持。从若干文件中获取数据组建文档的行为必须在HTML外部进行。HTML中解决这问题的最简单的方法是使用框架,但这是非常糟糕的用户界面,通常令用户迷惑和讨厌。

部分问题是HTML文档不能自然地插入到另一个HTML文档中,每个HTML文档有且仅有一个BODY,服务器端嵌入法仅能提供把HTML片段嵌入文档的能力,而不是嵌入有效的文档实体,此外服务器端提供的引用需依赖于服务器的存在,而不是真正的HTML文档部分。

然而,XML更加灵活。一个文档的基本元素没有必要与另一文档基本元素相同。即使两个文档共享同一基本元素,DTD也可声明元素对自身的包含。在适当的时候,XML标准并不制止结构完整的XML文档嵌入另一结构完整的XML文档的做法。

但是,XML走得更远一些,可定义一个机制,利用这机制可在若干本地或远程系统上的、较小的XML文档的基础上建立新的XML文档。语法分析器的任务就是按固定的序列把所有不同文档组合起来。文档中可包含另一文档,或许这个还包含其他文档。只要没有递归的文档包含行为(处理器可报告的错误),应用程序就仅能看见一个单一、完整的文档。从本质上说,这种机制提供客户端嵌入的功能。

XML而言,可使用外部通用实体引用的方法,在文档中嵌入另一文档。在DTD中,可按下述语法结构声明外部引用:

<!ENTITY name SYSTEM "URI">

URI就是Uniform Resource Identifier,与URL类似,但允许更加精确的资源链接规范。从理论上说,URI把资源与其储存位置分开,所以Web浏览器可以选择最近的或最空闲的几个镜像几个镜像站点,而无需明确指明该链接。URI领域的研究非常活跃,争论激烈,因此在实际应用中和在本书中,URI就是多用途的URL

例如,可能想在站点的每个页面中都放置相同的签字块。为明确所见,我们假设签字块为清单9-3所示的XML代码,而且假定可从URL http://metalab.unc.edu/xml/signature.xml.上获得该代码。

清单9-3XML签字文件

<?xml version="1.0">

<SIGNATURE>

<COPYRIGHT>1999 Elliotte Rusty Harold</COPYRIGHT>

<EMAIL>elharometalab.unc.edu</EMAIL>

</SIGNATURE>

DTD中添加如下声明,可把实体引用&SIG;与这个文件联系在一起:

<!ENTITY SIG SYSTEM "http://metalab.unc.edu/xml/signature.xml">

也可使用相关的URL。例如:

<!ENTITY SIG SYSTEM "xml/signature.xml">

如果被引用的文件放置于与引用该文件的文件所处的同一目录中,那么仅需使用一文件名进行引用。例如:

<!ENTITY SIG SYSTEM "signature.xml">

利用上述任一种声明,仅需使用&SIG;,就可在文档的任意位置引用签字文件的内容。如清单9-4中的简单的文档,图9-2显示的是在Internet Explorer 5.0中交付的文档。

9-2 使用外部通用实体引用的文档

清单9-4SIG外部通用实体引用

<?xml version="1.0" standalone="no">

<!DOCTYPE DOCUMENT [

<!ELEMENT DOCUMENT (TITLE, SIGNATURE)>

<!ELEMENT TITLE (#PCDATA)>

<!ELEMENT COPYRIGHT (#PCDATA)>

<!ELEMENT EMAIL (#PCDATA)>

<!ELEMENT SIGNATURE (COPYRIGHT, EMAIL)>

<!ENTITY SIG SYSTEM

"http://metalab.unc.edu/xml/signature.xml"?

]>

</DOCUMENT>

<TITLE>Entity references</TITLE>

&SIG;

</DOCUMENT>

注意外部实体引用的附加作用,因为文件不再完整,所以XML声明中的standalone属性值为no。解析文件表明该文件需要外部文件signature.xml中的数据。

9.5 外部参数实体

前述例子中使用单一的DTD,用于定义文档中所有的元素。然而文档越长,这种技术应用越少。此外通常希望将DTD中的部分内容用于许多不同的地方。

例如,对描述很少发生变化的邮件地址DTD来说,地址定义非常普遍,且可很方便地应用在不同的上下文中。类似地,清单9-2中列出的预定义实体引用可用于大部分XML文档中,但并不想总是对此清单进行拷贝和复制的操作。

可用外部参数实体把较小的DTD组成大型的DTD。也就是说,一个外部DTD可以链接到另一外部DTD,第二个DTD引入第一个DTD中声明的元素和实体。尽管严禁使用循环--若DTD2引用DTD1,则DTD1不能引用DTD2

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: