《XML实用大全》
1.4.1 超文本标记语言(Hypertext Markup Language)
1.4.2 级联样式单(Cascading Style Sheets)
1.4.3 可扩展的样式语言(Extensible Style Language)
2.1.1 化学标记语言(Chemical Markup Language)
2.1.2 数学标记语言(Mathematical Markup Language)
4.2.2 联赛(League)、(分部)Division和(球队)Team数据的XML化
7.5.2 其他字符集与Unicode字符集之间的转换...
10.7.2 在DTD中声明DIVISION和LEAGUE属性
12.11.4 background-attachment属性
14.9.2 使用xsl:element将元素插入到输出文档中
14.9.3 使用xsl:attribute将特性插入到输出文档中
14.19.3 使用xsl:stylesheet在文档中嵌入样式单
本部分包括以下各章:
第1章——XML概览
第2章——XML应用简介
第3章——第一个XML文档
第4章——数据的结构化
第5章——特性、空标记和XSL
第6章——结构完整的XML文档
第7章——外国语言与非罗马文字
本章将向读者介绍XML的基本知识以及概略地解释什么是XML以及如何使用XML。还要向读者说明如何将各种不同的XML表达式组合在一起,XML文档是如何创建的并如何向人们发送这种文档。
本章的主要内容包括:
· 什么是XML
XML代表Extensible Markup Language(eXtensible Markup Language的缩写,意为可扩展的标记语言)。XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识。它也是元标记语言,即定义了用于定义其他与特定领域有关的、语义的、结构化的标记语言的句法语言。
关于XML要理解的第一件事是,它不只是像超文本标记语言(Hypertext Markup Language,HTML)或是格式化的程序。这些语言定义了一套固定的标记,用来描述一定数目的元素。如果标记语言中没有所需的标记,用户也就没有办法了。这时只好等待标记语言的下一个版本,希望在新版本中能够包括所需的标记,但是这样一来就得依赖于软件开发商的选择了。
但是XML是一种元标记语言。用户可以定义自己需要的标记。这些标记必须根据某些通用的原理来创建,但是在标记的意义上,也具有相当的灵活性。例如,假如用户正在处理与家谱有关的事情,需要描述人的出生、死亡、埋葬地、家庭、结婚、离婚等,这就必须创建用于每项的标记。新创建的标记可在文档类型定义(Document Type Definition,在以后的篇幅中常简称为DTD)中加以描述。在本书的第二部分中将会学到有关DTD的更多的知识。现在,只需把DTD看作是一本词汇表和某类文档的句法。例如,在Peter Murray-Rust的Chemical Markup Language (化学标记语言,简写为CML)中的MOL.DTD文件中描述了词汇表和分子科学的句法:其中包括chemistry(化学)、crystallography(结晶学)、solid state physics(固体物理)等词汇。它包括用于atoms(原子)、molecules(分子)、bonds(化学键)、spectra(光谱)等的标记。这个DTD可与分子科学领域中的许多不同的人共享。对于其他领域也有其他的DTD,用户还可以创建自己的DTD。
XML定义了一套元句法,与特定领域有关的标记语言(如MusicML、MathML和CML)都必须遵守。如果一个应用程序可以理解这一元句法,那么它也就自动地能够理解所有的由此元语言建立起来的语言。浏览器不必事先了解多种不同的标记语言使用的每个标记。事实是,浏览器在读入文档或是它的DTD时才了解了给定文档使用的标记。关于如何显示这些标记的内容的详细指令是附加在文档上的另外的样式单提供的。例如,考虑薛定格(Schrodinger)方程:
![]()
科学论文中充满了这一类方程,但是科学家还必须等待多年,才能让浏览器的开发商支持书写最基本的数学公式所需的标记。音乐家也有同样的局限性,因为Netscape Navigator和Internet Explorer还都不支持乐谱。
有了XML就意味着不必等待浏览器的开发商来满足用户的需要了。用户可以创建自己需要的标记,当需要时,告诉浏览器如何显示这些标记就可以了。
关于XML要了解的第二件事是,XML标记描述的是文档的结构和意义。它不描述页面元素的格式化。可用样式单为文档增加格式化信息。文档本身只说明文档包括什么标记,而不是说明文档看起来是什么样的。
作为对照,HTML文档包括了格式化、结构和语义的标记。<B>就是一种格式化标记,它使其中的内容变为粗体。<STRONG>是一种语义标记,意味着其中的内容特别重要。<TD>是结构标记,指明内容是表中的一个单元。事实上,某些标记可能具有所有这三种意义。<H1>标记可同时表示20磅的Helvetica字体的粗体、第一级标题和页面标题。
例如,在HTML中,一首歌可能是用定义标题、定义数据、无序的列表和列表项来描述的。但是事实上这些项目没有一件是与音乐有关的。用HTML定义的歌曲可能如下:
<dt>Hot Cop
<dd> by Jacques Morali Henri Belolo and Victor Willis
<ul>
<li>Producer: Jacques Morali
<li>Publisher: PolyGram Records
<li>Length: 6:20
<li>Written: 978
<li>Artist: Village People
</ul>
而在XML中,同样的数据可能标记为:
<SONG>
<TITLE>Hot Cop</TITLE>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<LENGTH>6:20</LENGTH>
<YEAR> 978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
在这个清单中没有使用通用的标记如<dt>和<li>,而是使用了具有意义的标记,如<SONG>、<TITLE>、<COMPOSER>和<YEAR>等。这种用法具有许多优点,包括源码易于被人阅读,使人能够看出作者的含义。
XML标记还使非人类的自动机器人易于找出文档中的所有歌曲。在HTML中,机器人只能告诉我们这个元素是dt。机器人不能决定dt到底代表一首歌的题目还是定义,抑或只是一些设计者喜爱的缩进文本格式。事实上,单一文档中可以很好地包括带有三种意义的各种dt元素。
可以选择XML的元素名称,以便使其在附加的上下文中具有额外的意义。例如,元素名称可以是数据库的域名。XML比HTML更为灵活而且适用于各种应用,因为有限数目的标记不必用于许多不同的目的。
XML使许多只利用HTML难以解决的任务变得简单,使只利用HTML不可能完成的任务得以完成。因为XML是可扩展的,开发人员喜爱XML有许多原因。到底是哪个更令人感兴趣,取决于每个人的需要。但有一点是肯定的,一旦用上XML,就可发现,它正是解决许多令人感到棘手的问题的有力工具。本节研究一些令开发人员激动的一般应用。在第2章中,还会看到已经用XML开发出来的一些特殊应用。
XML允许各种不同的专业(如音乐、化学、数学等)开发与自己的特定领域有关的标记语言。这就使得该领域中的人们可以交换笔记、数据和信息,而不用担心接收端的人是否有特定的软件来创建数据。特定领域的开发人员甚至可以向本领域外的人发送文档,有相当的理由可以认为,至少接受文档的人能够查看文档的内容。
更进一步说,为特别的领域创建标记语言不会产生“病件”(bloatware)或是对于本专业外的人来说产生不必要的复杂性。一般人也许不会对电力工程图感兴趣,但是电力工程师却对此感兴趣。一般人也许不需要在他的Web页面中包括乐谱,但是作曲家却要这样做。XML让电力工程师描述他们的电路图,让作曲家写乐谱,而不会互相干扰。对于浏览器开发商来说,都不需要对特定的领域提供特殊的支持,也不需要提供复杂的插件。这一点现在已经实现了。
过去40年来的大多数计算机数据都丢失了,不是因为自然损害或是备份介质的磨损(虽然这也是一个问题,这个问题在XML中也没有解决),而只是因为没有人来写出如何读取这些数据介质和格式的文档。在十年前的5.25英寸的软盘上的Lotus 1-2-3文档在今天的大多数公司内都已经读不出来了。以不常用的格式保存的二进制数据,如Lotus Jazz 也许会永远地消失了。XML在基本水平上使用的是非常简单的数据格式。可以用100%的纯ASCII文本来书写,也可以用几种其他定义好的格式来书写。ASCII文本是几乎不会“磨损”的。丢失一些字节甚至是相当多的字节,剩下的数据还是可以读取的。这就与许多格式形成了鲜明的对比,如压缩数据或是串行的Java对象,这些数据即使丢失一个字节,剩余的数据也变得不可读取了。
从高水平上来说,XML是自描述的。假设在23世纪有一个信息考古学者,他在软盘上发现了如下一大段经过时间的“冲刷”而保存下来的XML代码:
<PERSON ID="p1100" SEX="M">
<NAME>
<GIVEN>Judson</GIVEN>
<SURNAME> McDaniel</SURNAME>
</NAME>
<BIRTH>
<DATE>2 Feb 1834</DATE> </BIRTH>
<DEATH>
<DATE>9 Dec 1905</DATE> </DEATH>
</PERSON>
即使这个考古学家不熟悉XML,但假设他可以讲20世纪时的英语,那么就可以很好地了解名为Judson McDaniel的人,此人出生在1834年2月21日,而死于1905年12月9日。事实上,数据中有一些空白或是损坏,还是可以得到这些信息。但对于专有格式的电子表格或是字处理程序的格式,就不是这么回事了。
更进一步说,XML有很好的规格文档。W3C的XML 1.0 规范和大量的论文书籍,如本书,都向人们准确地说明如何来阅读XML数据。没有什么秘密使得人们发生失误。
由于XML是非专有的并易于阅读和编写,就使得它成为在不同的应用间交换数据的理想格式。当前正在开发的一种这样的格式是Open Financial Exchange(开放财务交换,简写为OFX)格式。OFX是为个人财务程序,如Microsoft Money和Quicken交换数据而设计的。数据可以在程序间来回交换,还可以与银行、经纪事务所和其他机构交换数据。
有关OFX的内容将在第2章加以讨论。
正如上面所讨论的一样,XML使用的是非专有的格式,不受版权、专利、商业秘密或是其他种类的知识产权的限制。XML的功能是非常强大的,同时对于人类或是计算机程序来说,都容易阅读和编写。因而成为交换语言的首选。
使用XML而不是专有格式,人们就可以利用任何理解XML的工具来处理数据。还可以为不同的目的使用不同的工具。一个程序用来查看而另一程序用来编辑。XML使用户不必因为数据已经用专有格式编写好了或是接受数据的人只接受专有格式而限制在一个特定的程序上。
例如,许多出版商需要用Microsoft Word发稿。这就意味着大多数作者必须使用Word,即使他们更愿意使用WordPerfect或是Nisus Writer。因而这就使得其他出版字处理软件的公司陷入困境,除非他们的软件能够读写Word文件。由于要想达到这个目的,就得让开发人员反向了解未载入文档的Word文件格式,这使得在时间和资源上的投资大增。大多数其他字处理软件具有有限的读写Word文件的能力,但是通常都会丢失图形、宏、样式、修订标记和其他重要的特性。问题就在于Word文档的格式是不公开的专有格式,而且还在不断地变化。这样Word就成为最后的胜利者,即使作者更喜爱其他的更简单的程序。如果在XML中开发了一种通用的字处理格式,作者们就会使这个程序成为他们的首选程序。
XML对于大型和复杂的文档是理想的,因为数据是结构化的。这不仅使用户可以指定一个定义了文档中的元素的词汇表,而且还可以指定元素之间的关系。例如,如果要将销售客户的地址一起放在Web页面上,这就需要有每个客户的电话号码和电子邮件地址。如果向数据库中输入数据,可确保没有漏下的字段。还需要每部书都有一个作者。当没有数据输入时还可提供一个缺省值。XML也提供客户端的包括机制,可以根据多种来源集成数据并将其作为一个文档来显示。数据还可以马上进行重新排列。数据的各个部分可以根据用户的操作显示或隐藏。当处理大型的信息仓库,比如关系型数据库时是极为有用的。
从基本上来说,XML是一种文档格式。它是一系列的关于XML文档看起来是什么样子的规则。与XML标准的符合程度有两种级别。第一级是结构完整性,第二级是正确性。本书的第一部分向读者介绍如何编写结构完整的文档。而第二部分向读者介绍如何编写具有正确性的文档。
HTML是设计用于Internet上和Web页面内部的文档格式。正如本书所叙述的,XML当然也可以用在这些方面。但是XML具有更为广泛的适用性。正如前面所讨论的,可用于字处理器的保存文件的格式,可用于不同程序间的数据交换格式,可用作与Intranet模板一致化的工具,还可用作以人类可读的形式保存数据的手段。
虽然如此,如所有的数据格式一样,XML在有用之前也需要程序和内容。因而对于数据看起来应该是什么样子的,光了解XML本身还是不够的,这不光是一个规范所能解决的问题。用户还需要了解XML文档是如何编辑的,处理程序是如何读取XML文档并将其读取的信息传送给应用程序的,以及这些应用程序是如何处理数据的。
XML文档大多数情况下都是用编辑器创建的。编辑器可以是基本的文本编辑器如Notepad(记事本)或是vi,这些编辑器并不真正理解XML。另一方面,也可以用所见即所得的编辑器,如Adobe FrameMaker,这种编辑器可将用户完全隔离于XML底层格式之外。另外也可以是一个结构化的编辑器,如JUMBO,它可将XML文档显示为树状结构。对于最重要的部分,有趣的编辑器并不是太有用,因而本书将注意力集中于用普通的文本编辑器来编写XML文档。
其他程序也可以创建XML文档。例如,本书在讲述设计新的DTD的稍后章节中将可看到某些XML数据可直接从FileMaker的数据库中得出。在这种情况下,数据是先输入到FileMaker数据库中的,然后FileMaker的计算字段将数据转换为XML。一般来说,XML与数据库可协同工作得很好。
准确地说,我们可在第23章“设计新的XML应用”中看到这种情况。
无论在何种情况下,都是编辑器或其他程序创建了XML文档。通常,这一文档是某种计算机硬盘上的实际文件。但也不是必须如此。例如,文档可能是数据库中的记录或是字段,或者可能是从网络上接收来的字节流。
XML的语法分析程序(即所谓的XML处理程序)读取文档并检查其中包括的XML是否是结构完整的。它还要确定文档是否合法,虽然这种测试不是必需的。这种测试的详细情况将在本书的第二部分中讲述。如果文档通过了测试,则处理程序就将文档转换为元素的树状结构。
最后语法分析程序将树状结构或是树的节点传送给用户端应用程序。这个应用程序可能是浏览器,如Mozilla,或是其他能够理解如何处理数据的程序。如果这个应用程序是浏览器的话,数据就显示给用户。但是其他程序也可以接受数据。例如,可将数据翻译成数据库的输入、一系列要演奏的乐谱或是要运行的Java 程序。XML是非常灵活的,可以用于许多不同的目的。
总结一下,首先由一个编辑器创建了XML文档。语法分析程序将树状结构传送给浏览器,由浏览器显示出来。图1-1显示了这个处理过程。

图1-1 XML文档的处理流程
请注意,所有这些部分都是独立的,互相分离的。将这些部分联系在一起的是XML文档。改变编辑程序与终端应用程序无关。事实上,很可能在编写文档时就根本不知道最终的应用程序是什么。可能是最终用户来阅读文档,也可能是数据库从中提取数据,甚至还可能是未发明出来的程序,也可能是所有这些情况。文档与读取它的程序是无关的。
HTML也在某种程度上与读写它的程序无关,但是它只适用于浏览器。其他应用,如数据库输入已经不在它的有效范围之内了。例如,HTML没有提供某种方法来包括所需的内容,如每本书都必须有ISBN号码一样。在XML中可以包括这个。甚至可以强制安排元素出现的顺序(如第二级标题必须出现在第一级之后)。
XML并不是在真空中操作的。如果将XML用于不只是一种数据格式的话,就需要与多种相关的技术相互作用。这些技术包括为了向后兼容老式的浏览器的HTML、CSS(Cascading Style Sheet,级联样式单)和XSL(eXtensible Style Languages,可扩展的样式语言)、URL和URI、XLL(eXtensible Linking Language,可扩展的链接语言)和Unicode字符集。
Mozilla 5.0和Internet Explorer 5.0是首先对XML提供支持(虽然并不完全)的浏览器。但是,要使大多数用户升级到这两种浏览器的新版本上来,可能还要花两年的时间。(我的妻子Beth在1999年还在使用Netscape 1.1。)因而在今后一段时间内,还需要将XML内容转化为经典的HTML。
因而,在转向XML之前,对使用HTML还不应感到别扭。用户不必完全成为一个时髦的图形设计者,但是应该了解如何将一个页面与另一个页面链接起来,了解如何在文档中包括图像,如何使文本变成粗体等等。由于HTML是XML的最普通的输出格式,所以对HTML了解得越多,也就越容易了解如何创建所需的效果。
另一方面,如果已经熟悉了利用表格或是单像素的GIF来安排页面上的对象,或是如果开始借助于画出草图而不是借助于内容来创建Web站点的话,那么也就必须要忘记某些坏的习惯。正如前面所讨论的一样,XML将文档的内容与文档的外观相分离。首先开发内容,然后再用样式单将格式附加其上。将内容与样式分开是非常有效的技术,这既改善了文档内容也改善了文档外观。除此之外,还允许作者和设计者更加互相独立地工作。但是,对于设计Web站点来说,确实需要有不同的思路,如果涉及多人的话,或许要利用不同的项目管理技术。
由于XML允许在文档中包括任意的标记,所以对于浏览器来说,没有办法事先知道如何显示每个元素。当将文档送给用户时,还要向用户发送样式单,通过样式单告诉浏览器如何格式化每个元素。可以使用的一种样式单是级联样式单( Cascading Style Sheet ,简写为CSS)。
CSS开始是为 HTML设计的,它定义字号、字族、字重、段落缩进、段落对齐和其他样式等格式化属性,这些属性都可以施加到个别的元素上。例如,CSS允许HTML文档来指定所有的H1元素应该被格式化为32磅、中间对齐的Helvetica字体的粗体。单独的样式可以施加到大多数HTML标记上,它能够覆盖浏览器的缺省设置。多个样式单可施加到一个文档上,而多个样式也可用于单个元素上。样式根据特定的一套规则级联起来。
CSS规则和属性将在第12章“级联样式单,第一级”和第13章“级联样式单,第二级”中详细介绍。
向XML施加CSS规则是很容易的。只要改变施加规则于其上的标记名称即可。Mozilla 5.0直接支持CSS样式单与XML的结合,虽然到目前为止,此浏览器时常发生崩溃。
可扩展的样式语言(Extensible Style Language,简写为XSL)是更为先进的专门用于XML文档的样式单语言。XSL文档本身就是结构完整的XML文档。
XSL文档包括一系列的适用于特定的XML元素样式的规则。XSL处理程序读取XML文档并将其读入的内容与样式单中的模式相比较。当在XML文档中识别出XSL样式单中的模式时,对应的规则输出某些文本的组合。与级联样式单不同,输出的文本比较任意,也不局限于输入文本加上格式化信息。
CSS只能改变特定元素的格式,也只能以元素为基础。但XSL样式单可以重新排列元素并对元素进行重排序。这种样式单可以隐藏一些元素而显示另外一些元素。更进一步说,还可以选择应用样式的标记,而不仅是基于标记的,而且还基于标记的内容和特性,还基于标记在文档中相对于其他元素的位置,以及基于各种其他的准则。
CSS的优越性在于具有广泛的浏览器支持。但是XSL更为灵活和强大,可更好地适用于XML文档。而且带XSL样式单的XML文档可以很容易地转换为带CSS样式单的HTML文档。
XSL样式单将第14章“XSL变换”和第15章“XSL格式化对象”中更为详细地论述。
XML文档可用于Web,正如HTML和其他文档一样。使用时,也如HTML文档一样,被统一资源定位符(Uniform Resource Locator,简写为URL)所引用。例如,在URL http://www.hypermedic.com/style/xml/tempest.xml处,可以找到以XML标记的莎士比亚的歌剧tempest的全文。 虽然URL已被人们广泛理解并被广泛支持,但XML规范使用的是更为通用的统一资源标识符(Uniform Resource Identifier,简写为URI)。URI对于定位Internet上的资源是更为通用的架构,更为注重资源而不太注重位置。理论上说,URI可找出镜像文档的最为近似的副本或是找出已经从一个站点移动到另一站点的文档。实际上,URI仍然处于进一步的研究之中,被当前的软件所唯一支持的一种URI正是URL。
只要将XML张贴到Internet上,用户当然希望能够对此文档寻址并且可以将这些文档链接起来。标准的HTML链接标记可用在XML文档中,而且HTML文档也可与XML文档加以链接。例如,下面的HTML代码将链接指向了前文提到的以XML形式出现的Tempest的副本:
<a href="http://www.hypermedic.com/style/xml/tempest.xml">
The Tempest by Shakespeare
</a>
如果用户跟随着链接,浏览器能否显示这个文档,依赖于该浏览器处理XML文件的能力。目前大多数浏览器还不能很好地处理XML文档。
然而,XML利用XLink来与文档链接,用XPointer来确定文档个别部分的位置,就可以有更多的功能。.
XLink使任意元素成为链接,而不只是A元素。进一步说,链接可以是双向的、多向的或是指向多个镜像的站点,并选择这些站点中最近的一个。XLink利用普通的URL来标识它链接的站点。.
XLink将在第16章中加以讨论。
XPointer能使链接不仅指向特定位置处的特定文档,而且还可指向特定文档的特定部分。XPointer可以引用文档中的特定的元素,如第一个、第二个或是第十七个特定的元素。XPointer提供了文档间连接的非常强大的功能,而这些文档不必有包括附加标记的目的文档,正因为如此,其中的个别部分才可以被链接。
进一步说,与HTML的锚(anchor)不同,XPointer不只是引用文档中的一点。XPointer可以指向一个范围或是一个区域。因而XPointer可以用来选择文档的特定部分,或许这样一来,就可以将这部分复制或是将其装入其他程序。
XPointer将在第17章中加以讨论。
Web是国际性的,到目前为止其上主要文本部分仍为英文。XML是改变这种状况的开始。XML对双字节的Unicode字符集及其紧凑的表示提供了完全的支持。这一字符集几乎可以支持地球上的每一种常用的字符。遗憾的是,光有XML还是不够的。为了阅读一种文字,需要三个条件:
1. 该种文字的字符集
2. 该字符集的字体
3. 操作系统和应用软件能够理解这种字符集
如果想要以这种文字写作,并阅读这种文字,还需要该种文字的输入法。当然,XML定义了字符引用,可使用户使用纯ASCII字符将未列在本地字符集中的字符加以编码。这对于偶尔引用一下希腊或是中文字符也足够了,当然不能指望用这种办法以其他语言来写一部小说。
在第7章“外国语言和非罗马文本”中,读者将会看到国际文本在计算机中是如何来代表的,XML如何来理解文本,以及如何来利用不得不以非英语来读写的软件。
XML定义了一些标记的语法规则,可用来标记文档。XML文档是用XML标记来标记的。XML文档的缺省编码方法是Unicode。
XML文档的许多好处之一是,可以包括与其他文档和资源的超链接。这些链接是根据XLink规范创建的。XLink用URI(理论上)或是用URL(实际上)标识出链接的文档。一个XLink可进一步指定它所链接文档的个别部分。这些个别部分是通过XPointer来寻址的。如果打算由人来阅读XML文档,那么样式单就提供个别元素格式化的指令(并不是所有的XML文档都如此)。样式单可用几种样式语言中的任一种来编写。CSS和XSL是两种最常用的样式语言,虽然也存在其他基于XSL的样式语言,如DSSSL(Document Style Semantics and Specification Language,文档样式语义和规格语言)。
我已经在本章中概述了许多令人激动的技术。但是,良知让我告诉读者,我还没有全讨论到。事实上,我所叙述的大部分是XML的前景而不是当前的现实。XML让软件产业中的许多人激动不已,许多程序员正在奋发工作,以便将梦想变为现实。层出不穷的新软件正将我们带入XML的“天堂”,但是由于这一领域非常新,许多新软件还没有经过充分地考验。在本书的其余部分,我将小心地不仅要指出什么将可能出现,而且也指出什么实际已经上出现了。令人沮丧的是,这两件事常常不是一回事。不管怎么说,当前还是可以小心地用XML来做一些实际工作的。
在本章中,读者了解了某些XML可以为我们做的事情。更明确地说,了解了以下几个方面:
· 一种能够为特定文档和领域创建标记语言的元语言。
· XML的起因是,用户受到SGML复杂性的挫伤和HTML的不充分。
在以下几章中,读者可以看到几个XML应用,学到某些将XML用到现实中的方式。例子包括音乐乐谱、数学、化学、人力资源、Web广播以及其他一些应用。
在本章中,我们将要查看XML的几个应用实例、用来进一步改进XML的标记语言和在后台使用的XML。看一看XML的某些应用,即使只是发展的初级阶段,也是令人鼓舞的。本章将向读者讲述XML的广泛应用性的某些看法。在我写作本书时,更多的XML应用正在创建并与其他格式的应用接轨。
第五部分更为详细地讲述了本章中讨论过的一些XML应用程序。
本章的主要内容包括:
· 什么是XML应用程序
XML是一种元标记语言,可用来设计与特定专业领域有关的标记语言。每种基于XML的标记语言都叫做XML应用程序。这种应用不是像Mozilla Web浏览器、Gnumeric电子表格或 XML Pro那样的编辑器一样地使用XML,而是在特定的领域中应用XML,如化学上用的化学标记语言(Chemical Markup Language,简写为CML)或是家谱上用的GedML。每种XML应用程序有它自已的句法和词汇表。这种句法和词汇表遵守XML的基本规则。
这有点像人类语言,每种语言都有它们自己的词汇表和语法,但同时遵循人体解剖学和大脑结构所要求的基本规则。
XML是以文本数据为基础的非常灵活的格式。在本章中讨论的广泛的应用都选择了XML作为基础的原因是(排除大肆宣传的因素),XML提供了切合实际的并清楚地描述了的易于读写的格式。应用程序将这种格式用于它的数据,就能够将大量的处理细节让几个标准工具和库函数去解决。更进一步说,对于这样的程序也容易将附加的句法和语义加到XML提供的基本结构之上。
Peter Murray-Rust的化学标记语言(Chemical Markup Language,简写为CML)可能是第一个XML应用。CML原来是要发展成SGML应用的,但随着XML标准的发展,逐步演化成了XML。在CML的最简单的形式下,CML是“HTML加分子”,但是它的用处却超出了Web的范围。
分子文档常常包括成千上万个不同的详细的对象。例如,单个中等大小的有机分子可能含有几百个原子,每个原子有几个化学键。CML寻求以一种直接方式组织这种复杂的化学对象,以便能够让计算机理解,并显示和能够加以检索。CML可以用于分子结构和序列、光谱分析、结晶学、出版、化学数据库和其他方面。它的词汇表包括分子、原子、化学键、晶体、分子式、序列、对称、反应和其他化学术语。例如,清单2-1是描述水(H2O)的基本CML文档:
清单2-1:水分子H2O
<?xml version="1.0"?>
<CML>
<MOL TITLE="Water">
<ATOMS>
<ARRAY BUILTIN="ELSYM">H O H</ARRAY>
</ATOMS>
<BONDS>
<ARRAY BUILTIN=”ATID1”>1 2</ARRAY>
<ARRAY BUILTIN=”ATID2”>2 3</ARRAY>
<ARRAY BUILTIN=”O DE ”>1 1</ARRAY>
</BONDS>
</MOL>
</CML>
CML提供的对传统的管理化学数据的方法的最大改善在于数据的检索。CML还使得复杂的分子数据可在Web上发送。由于XML的底层是与平台无关的,所以可以避免由于使用不同的平台而引起的二进制格式不兼容的问题,这种问题在使用传统的化学软件和文档(如Protein Data Bank (PDB)格式或者MDL Molfiles)时常常可以遇到。
Murray-Rust还创建了第一个通用目的的XML浏览器JUMBO。图2-1是JUMBO正在显示的一个CML文件。Jumbo将每个XML元素赋给能够显示这些元素的Java类。为了使Jumbo支持新的元素,只要编写用于该元素的Java类即可。Jumbo是与显示基本的一套CML元素(其中包括分子、原子和化学键)的类一起发布的。Jumbo可从http://www.xml-cml.org/ 站点处得到。
传说CERN的Tim Berners-Lee发明了World Wide Web和HTML,这样一来,高能物理学家们就可以交换论文和印前出版物了。从我个人角度来说,我从不相信这个传说。我是学物理学的,而且我曾在物理、应用数学、天文学和计算机科学等几个学科之间徜徉多年。这几个学科的论文有一点是共同的,就是论文中充满了大量的方程。直到目前为止,Web已经出现了有九年时间了,还没有找到一种在Web页面上包括方程的好办法。
现在有几种办法如Java小程序,可以分析自定义的句法,还有一种转换程序,可将用LaTeX软件编辑的方程转化为GIF图像,另一种是自定义的浏览器,可以读取TeX文件,但所有这些办法都不能产生高质量的结果,而且这些都不能满足Web作者(即使是科学领域的作者)的需求。最终,只有XML才能开始改变这种状况。

图2-1 显示CML文件的JUMBO浏览器
数学标记语言(Mathematical Markup Language,MathML)是一种用于数学方程的XML应用。MathML具有足够的能力来处理大多数形式的数学问题从初中的算术到微积分和微分方程。它也可以处理许多更为高级的课题,但还存在一些空白,如在某些数学的分支中使用的更为高级也更为晦涩的记号。虽然对于MathML来说,在纯数学和理论物理的高端还有局限性,但是却足以处理几乎所有的教育、科学、工程、商业、经济和统计学上的要求。而且将来MathML必然要加以扩展,因而可以认为,即使是最纯粹的数学和纯理论的理论物理都能够在Web上出版和进行研究工作。MathML完成了Web向着科学研究和通信方面的有用工具方向的发展 (尽管说它也适用于作为新媒体来制作广告小册子有点离题太远)。
Netscape Navigator和Internet Explorer还不支持MathML。但是许多数学家都抱着热烈的希望,希望这些浏览器在不久的将来能够对此加以支持。W3C已经将某些对MathML的支持集成到他们的浏览器测试平台Amaya中了。图2-2是Amaya显示的用MathML编写的Maxwell方程的协变形式。
Amaya软件可以在本书所附CD-ROM的browsers/amaya目录中找到。

图2-2 Amaya浏览器显示的用MathML编写的协变形式的Maxwell方程
清单2-2列出了Amaya浏览器正在显示的XML文件:
清单2-2:MathML中的麦克斯韦(Maxwell)方程
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/TR/REC-html40"
xmlns:m="http://www.w3.org/T / EC-MathML/"
>
<head>
<title>Fiat Lux</title>
<meta name="GENERATOR" content="amaya V1.3b" />
</head>
<body>
<P>
And God said,
</P>
<math>
<m:mrow>
<m:msub>
<m:mi>δ</m:mi>
<m:mi>α</m:mi>
</m:msub>
<m:msup>
<m:mi>F</m:mi>
<m:mi>αβ</m:mi>
</m:msup>
<m:mi></m:mi>
<m:mo>=</m:mo>
<m:mi></m:mi>
<m:mfrac>
<m:mrow>
<m:m >4</m:m >
<m:mi>π</m:mi>
</m:mrow>
<m:mi>c</m:mi>
</m:mfrac>
<m:mi></m:mi>
<m:msup>
<m:mi>J</m:mi>
<m:mrow>
<m:mi>β</m:mi>
<m:mo></m:mo>
</m:mrow>
</m:msup>
</m:mrow>
</math>
<P>
and there was light
</P>
</body>
</html>
清单2-2是混合使用HTML/XML的页面的例子。其中文本(“Fiat Lux”、“Maxwell’s Equations”、“And God said”、“and there was light”)的标题和段落是用经典的HTML编写的。实际的方程是用MathML编写的,这是一个XML应用。
一般来说,这种混合页面需要浏览器的特殊支持,这里也正是这种情况,否则就得有插件、ActiveX控件或是JavaScript程序来分析和显示内嵌的XML数据。当然最终用户需要像Mozilla 5.0或是Internet Explorer 5.0这样的浏览器,这两种浏览器可以分析和显示纯XML文件,而不需要HTML作为中介。
Microsoft的频道定义格式(Channel Definition Format,简写为CDF)是用于定义频道的XML应用。Web站点使用频道向预订站点的用户传送信息,一改过去那种坐等用户前来浏览并获取信息的状况。这也叫做Web广播或是“推”。CDF首先是在Internet Explorer 4.0中引入的。
CDF文档是一个XML文件,与被推的站点的HTML文件分别存放,但是却链接到此HTML文件上。CDF文档中的频道定义决定了要发送哪个页面。页面可以通过发送通知向预订者加以推送,但也可以发送整个站点,或是由阅读者在方便的时候自己来“拉”信息。
用户可向自己的站点添加CDF,而不用改变现存的所有内容。只要在页面上添加与CDF文件的一个不可见的链接即可。当浏览者访问这个页面时,浏览器显示一个对话框,询问浏览者是否要预订频道。如果浏览者选择了预订,则浏览器就下载描述频道的CDF文档。然后浏览器将CDF文档用指定的参数与用户自己的优选项结合起来,以便决定什么时候检查服务器上的新内容。这实际上不是真正的“推”,因为客户必须初始化连接,但是这确实是在没有浏览请求的情况下发生的。图2-3是IDG的Active Channel(活动频道)显示在Internet Explorer 4.0中的情况。

图2-3 在Internet Explorer 4.0中显示的IDG的Active Channel(活动频道)
在第21章“用CDF推送Web站点”中将详细地讨论CDF。
Internet Explorer 4.0可在本书所附CD-ROM上的browsers/ie4目录中找到。
Jon Bosak曾经将Shakespeare(莎世比亚)的全部话剧翻译成了XML。这些剧本的全文都包括其中了,用XML标记来区分剧名、每幕标题、舞台指导、对白、台词、旁白等。
莎世比亚的全套话剧可以本书所附CD-ROM上的examples/shakespeare目录中找到。
读者可能要问,对于一本书或是一个普通的文本文件来说,这样做有什么好处呢?对于人类读者来说,这没有什么不同,但对分析文字的计算机来说,这样做就使得容易区分组成话剧的不同元素。例如,要让计算机在全文中找出Romeo(罗密欧)的台词就变得简单了。
进一步说,借助于改变格式化文档的样式单,某个演员就很容易地打印出该剧的一个副本,其中他(她)的所有台词都格式化为粗体,而他(她)前面和后面的台词都用斜体来表示。另外还可以想像出来的事是,将剧本分成不同人的道白时,利用XML格式化的版本也比原来的文本要容易得多。
Bosak曾经将新旧约全书、古兰经和摩门教教义的英文译本用XML加以标记。这些书中的标记有些不同。例如,它并不对讲话人加以区分。因而(比如说)也就不能利用这种特殊的XML文档来创建带红色字母的圣经,虽然使用不同的一套标记可以达到这一目的。(带红色字母的圣经将耶稣说的话用红色印刷。)而且由于这些文件是用英语写成的,而不是原来的语言,这对于学术上的文本分析来说,就不是那么有用了。如果时间和资源允许的话,只要愿意,用XML来书写原文也是可以办得到的。这时只要设计一套与Bosak使用的不同,但却是描述同样的数据的词汇表和句法即可。
经XML标记了的圣经、古兰经和摩门教教义都可在本书所附的CD-ROM上的examples/religion目录中找到。
XML对于文本数据来说是最通用的格式。它所用于的某些事物还进一步地完善了XML本身。这包括XSL样式单语言、XLL链接语言和用于XML的文档内容描述(Document Content Description,简写为DCD)。
XSL(Extensible Style Language,可扩展的样式语言)本身就是XML应用。XSL有两个主要部分。第一部分定义了将XML文档加以转换的词汇表。这一部分的XSL包括用于树的XML标记、节点、式样、模板和其他用于将XML文档从一种标记词汇转换成另一种(或是同一种却以不同的顺序)所需要的元素。
XSL的第二部分定义了用于格式化转换后的XML文档(由第一部分产生的)的词汇表。这包括用于格式化对象(如分页、块、字符、列表、图形、方框、字体和其他)的XML标记。清单2-12中列出了一个典型的XSL样式单:
清单2-12:一个XSL样式单
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/T /WD-xsl"
xmlns:fo="http://www.w3.org/T /WD-xsl/FO"
result-ns="fo">
<xsl:template match="/">
<fo:basic-page-sequence >
<xsl:apply-templates/>
</fo:basic-page-sequence>
</xsl:template>
<xsl:template match="ATOM">
<fo:block font-size="10pt" font-family="serif" space-before="12pt">
<xsl:value-of select="NAME"/>
</fo:block>
</xsl:template>
</xsl:stylesheet>
我们将在第14章和15章中详细讨论XSL。
可扩展的链接语言(Extensible Linking Language,简写为XLL)定义了新的名为XLink的更一般种类的链接。XLinks可完成用HTML中的以URL为基础的超链接所能完成的所有任务。例如,脚注元素可像下例一样直接链接注解的文本:
<footnote xlink:form="simple" href="footnote7.xml">7</footnote>
进一步说,XLink可以做HTML链接不能做的事。XLink可以是双向的,因而读者可以返回原来所在的页面(跳转前所在页面)。XLink可以链接到文档中的任意位置。XLink可将文本或是图形数据嵌入文档内部,而不需要用户去激活链接(更像HTML中的<IMG>标记,但更灵活)。简短说,XLink使超链接的功能更为强大。
在第16章“XLink”中将要更加详细地讨论XLink方面的内容。
XML的用于声明XML元素内容应该如何格式化的工具对于不存在的内容显得功能不足。例如,假设作为数据的一部分,像下面一样建立了MONTH元素:
<MONTH>9</MONTH>
我们能看到MONTH元素的内容应该是字符数据。我们不能说必须给这个元素以从1到12的整数。
已经提出了几种XML本身的方案,以便更严格地限制什么可以出现在任意给定的内容中。有一种方案就是文档内容描述(Document Content Description,简写为DCD)例如,这里有一个DCD,声明了MONTH元素只能含有1到12的整数:
<DCD>
<ElementDef Type="MONTH" Model="Data" Datatype="i1"
Min="1" Max="12" />
</DCD>
我还可以向读者展示好多的用于XML的XML的例子,但是上例已经表明了基本的观点:XML强大得足以来描述和扩展本身。此外,这还意味着,XML规范可以保持短小和简单。完全可以没有XML 2.0,因为任何主要的所需的附加内容都可以根据原来的XML加以建立,而不必成为XML的新功能。需要加强功能的人们和程序员们可以使用这些新功能,而不需要的人可以将其忽略。用户不必了解什么是不使用的。XML提供了“砖和泥”,利用这些“砖和泥”既可以建起“小屋”也可以建起高耸的“城堡”。
并不是所有的XML应用都是公开的、开放的标准。有许多软件开发商正在将其自身的数据转向XML,只是因为XML是被公众很好理解的、通用目的的格式,可以用容易获得的、便宜或免费的工具加以处理。
Microsoft Office 2000已将HTML变为与它的内建二进制格式同等的格式。不过, HTML 4.0还不能提供对Office所需的所有功能的全面支持,如修订跟踪、脚注、批注、索引和术语表项等等。不能用HTML表达的附加数据嵌入到XML的小型代码块中。Word的矢量图形保存在VML中。在这种情况下,嵌入的XML在标准的浏览器中的不可见性是个关键因素。
Federal Express公司将详细跟踪的信息用作为与其他送货公司(如UPS(美国快寄服务公司和Post Office(邮局))相比更有竞争力的优点。首先这种信息来源于顾客软件,然后是通过Web。最近,FedEx公司开始对其API(应用程序接口)和库函数(第三方和内部开发者可使用这些API将他们的软件和系统与FedEx的加以集成)的?测试。这种服务的数据格式就是XML。
Netscape Navigator 5.0 支持XML在Web浏览器上的直接显示,但是,Netscape 实际在内部早在4.5版时就已经开始使用XML了。当用户请求Netscape显示与当前站点相联系的站点的列表时,浏览器就连接到运行在Netscape服务器上的一个CGI程序上。服务器送回来的数据就是XML。清单2-13就是与站点http://metalab.unc.edu/相联系的站点的XML数据:
清单2-13:与http://metalab.unc.edu/相联系的站点的XML数据
<?xml version="1.0"?>
<RDF:RDF>
<RelatedLinks>
<aboutPage
href="http://in fo.netscape.com/fwd/rl/http://metalab.unc.edu:80/*">
</aboutPage>
<child instanceOf="Separator1"></child>
<child
href="http://info.netscape.com/fwd/rl/http://www.sun.com/"
name="Sun Microsystems">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://www.unc.edu/"
name="Unc">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://sunsite.sut.ac.jp/"
name="SunSITE Japan">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://sunsite.nus.sg/"
name="SunSITE Singapore">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://sunsite.berkeley.edu/"
name="Berkeley Digital Library SunSITE">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://www.sun.com/sunsite"
name="SunSITE on the net">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://www.sunsite.auc.dk/"
name="SunSITE Denmark">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://sunsite.edu.cn/"
name="SunSITE China">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://sunsite.stanford.org/"
name="Stanford University SunSITE">
</child>
<child
href="http://info.netscape.com/fwd/rl/http://www.cdromshop.com/
cdshop/desc/p.061590000085.html" name="SunSITE Archive">
</child>
<child instanceOf="Separator1"></child>
<child instanceOf="Separator1"></child>
<child href="http://home.netscape.com/escapes/smart_browsing"
name="Learn About Smart Browsing...">
</child>
</RelatedLinks>
</RDF:RDF>
这一切都完全发生在幕后。用户决不会知道那些数据正在用XML加以传送。实际上显示的是Netscape Navigator中的菜单,而不是XML或HTML页面。
这些实际上还只是将XML用于内部数据的不成熟的表面现象。许多其他使用XML的项目还刚刚起步,还有一些项目将在明年起步。大多数这样的项目不会受公开注意,也不会在商业出版物上受到吹捧,但是不管怎样,在其项目存活期内它们都具有潜力可为公司节约成千上万美元的开发费用。XML的自说明性对于公司内部的数据也是很有用的。例如,许多公司现在正在匆忙地设法找出20年前退休的程序员是否用了两位数字的日期。如果你正在干这样的事情,你是愿意将数据写成下面的样子呢:
3c 79 65 61 72 3e 39 39 3c 2f 79 65 61 72 3e
还是下面的样子:
<YEAR>99</YEAR>
不幸的是,许多程序员现在还坚持将数据写成第一种格式。XML还可使错误容易发现和修改。
本章只是刚刚接触到已经和将要使用XML的应用。一些应用,如CML、MathML和MusicML很明显是用于Web浏览器的HTML扩展。但是许多别的应用,如OFX、 XFDL和HRML完全走的是另一条路。所有这些应用都有建立在XML之上的自己的语义和句法。在某些情况下,XML的“根”是很明显的,但在另外一些情况下,即使在其上工作达一月之久,也不一定会发现它与XML有什么关系。在本章中,我们讨论了下面的可使用XML的应用:
· 使用CML的分子科学
在下一章中,读者将要学习编写自己的XML文档并在Web浏览器上加以显示。
本章教读者用自己定义的可为文档所理解的标记来创建简单的XML文档。读者将学到如何编写样式单,以便用于在文档中描述标记内容如何显示。最后,还要学到如何将文档装到Web浏览器中以便查看。
由于本章利用示例来加以讲解,而不是从原理出发,因而不会涉及许多细节。有经验的读者将会注意到几处例外和特殊情况没有在本章加以讨论。对此不必担心。在下几章中将会讨论到。对于大部分内容,不必太关心技术内容。正如HTML一样,也可通过复制其他人创建的简单的示例并按自己的需要加以修改来学习。
为了达到上述目的,我鼓励大家按我在本章中给出的示例键入程序逐步进行,并将这些代码装入讨论过的不同的程序中。这将使读者对XML产生基本感受,这将使在未来几章中提到的技术细节在特定示例的环境中容易掌握。
本章的主要内容包括:
· 创建简单的XML文档
本节遵照老程序员介绍新语言的传统,先用一个能够在屏幕上打印出“Hello World”的程序加以介绍。XML是标记语言,而不是编程语言,但是基本原理还是适用的。最简单的方法是以一个完全的可运行的有扩展能力的示例开始,而不要尝试以更基本的无任何功能的程序开始。如果用户在使用基本的工具时确实遇到了问题,在简短的文档环境中也比在复杂的文档环境下更容易调试和改正。
在本节中,读者将学到如何创建一个简单的XML文档并将其保存在文件中。然后我们对其中的代码及其意义再加以仔细考察。
在本节中,读者将学到如何键入一个实际的XML文档。我们从能够想像得到的最简单的XML文档开始。这个文档列在清单3-1中:
清单3-1:Hello XML
<?xml version="1.0" standalone="yes"?>
<FOO>
Hello XML!
</FOO>
这虽然不太复杂,但却是一个“好”的XML的文档。更准确地说,这是一个结构完整的XML文档(XML中有一些用于文档的专门术语,依照到底满足了哪条规则而被认为是“好”的 。其中“结构完整的”就是一条这样的术语,在本书的后面要对此加以讨论。)可在任何使用方便的文本编辑器,如Notepad、BBEdit或是emacs中键入这个文档。
结构完整性将在第6章“结构完整的XML文档”中加以讨论。
当键入了上面的代码之后,请将该文档保存在名为hello.xml的文件中。也可以使用诸如HelloWorld.xml、MyFirstDocument.xml或是其他文件名,但三个字母的扩展名.xml是标准的,一般不要更改。而且还要确保以普通的文本格式加以保存,而不要用某些字处理程序,如WordPerfect或Microsoft Word的内建格式。
如果使用的是Windows 95/98上的Notepad来编辑文件,当保存文档时,一定要将文件名用双引号括起来,即“Hello.xml”,而不要只是Hello.xml,正如图3-1所示的一样。如果没有引号,Notepad会在文件名后再加上.txt扩展名,也就是文件名变成了Hello.xml.txt,这完全不是我们所希望出现的。

图3-1 在Notepad中用带引号的文件名来保存XML文档
Windows NT版本的Notepad还会给出将文件保存为Unicode格式的选项。令人惊奇的是,这样保存也可以,不过我们还是坚持使用基本的ASCII文本格式比较好。XML文件既可以是Unicode格式也可以是Unicode的名为UTF-8的压缩版本,这是严格的ASCII的超集,因而纯ASCII文件也是合法的XML文件。
UTF-8和ASCII将在第7章“外国语言和非罗马文本”中加以更为详细的讨论。
既然已经创建了第一个XML文档,当然想看一看了。这个文件可以在支持XML的浏览器,如Internet Explorer 5.0中直接打开。图3-2显示的就是结果。
我们看到的结果将依不同的浏览器而有所不同。在本例情况下,文件是格式化得很好的,以不同的颜色来表示不同的句法。不过所看到的并没有吸引人的地方。问题在于浏览器并不了解如何来处理FOO元素。我们必须指示浏览器如何来处理每个元素,这就要用到样式单了。我们将要简单地介绍一下,但首先还是仔细地考察一下这个文档。

图3-2 hello.xml在Internet Explorer 5.0中的显示结果
让我们检查一下列在清单3-1中的这个简单的XML文档,以便更好地理解每行代码的意义。第一行是XML声明:
<?xml version="1.0" standalone="yes"?>
这是XML处理指令的例子。处理指令以<?开始,而以?>结束。在<?后的第一个单词是处理指令名,在本例中是xml。
XML声明有version和standalone两个特性。特性是由等号分开的名称-数值对。位于等号左边的是特性名,而其值位于等号的右边,并用双引号括起来。
每一个XML文档都以一个XML声明开始,用以指明所用的XML的版本。在上例中, version特性表明这个文档符合XML 1.0规范。XML声明还可以有standalone特性,这告诉我们文档是否在这一个文件里还是需要从外部导入文件。在本例中,以及在以后的几章中,所有的文档都在一个文件里完成,因而standalone特性的值要设置为yes。
现在让我们看一下清单3-1中的下面的三行:
<FOO>
Hello XML!
</FOO>
总体上说,这三行组成了FOO元素。分开说,<FOO>是开始标记,而</FOO>是结束标记,Hello XML!是FOO元素的内容。
读者可能要问,<FOO>标记的意义是什么?回答是“你要让它是什么就是什么”。除了几百个预定义的标记之外,XML还允许用户创建所需的标记。因而<FOO>标记可以具有用户赋于的任何意义。同一个XML文档可以用不同的标记名编写,正如清单3-2、3-3和3-4所表明的:
清单3-2:greeting.xml
<?xml version="1.0" standalone="yes"?>
<GREETING>
Hello XML!
</GREETING>
清单3-3:paragraph.xml
<?xml version="1.0" standalone="yes"?>
<P>
Hello XML!
</P>
清单3-4:document.xml
<?xml version="1.0" standalone="yes"?>
<DOCUMENT>
Hello XML!
</DOCUMENT>
清单3-1到3-4这四个文档用的标记名各不相同,但都是等价的,因为具有相同的结构和内容。
标记可有三类意义:结构、语义和样式。结构将文档分成元素树。语义将单个的元素与外部的实际事物联系起来。而样式指定如何显示元素。
结构只是表达文档的形式,而不管单个标记和元素间的差别。例如,上面清单3-1到3-4中的四个XML文档结构是相同的。它们都指定文档具有一个非空的基本元素。标记的不同名称没有结构上的意义。
语义的意义存在于文档之外,在作者的心中或是读者或是某些生成或读取这些文件的计算机程序中。例如,理解HTML但不理解XML的Web浏览器,可能会将段落的意义赋给<P>和</P>标记,但不会赋给标记<GREETING>和</GREETING>、<FOO>和 </FOO>或是<DOCUMENT>和</DOCUMENT>。讲英语的人可能会比<FOO>和</FOO>或<P>或</P>更容易理解<GREETING>和</GREETING>或是<DOCUMENT>和</DOCUMENT>的意义。正如“美丽”的意义存在于观察者心中。
计算机作为一个哑机器,不能说是真正地理解任何事物的意义。计算机只是根据预先确定的公式来处理位和字节而已(虽然非常快)。对于一台计算机而言,用<FOO>或是<P>与使用<GREETING>或<DOCUMENT>标记没有什么差别。即使对于Web浏览器来说,也不能说它理解什么是段落。所有的浏览器了解的是,当遇到一个段落时,在下一个元素前面要放置一个空行。
自然地,使标记的名称能够尽可能反映其包含的意义更好一些。许多学科,如数学和化学正在创建该学科的工业标准和标记集。如果合适的话,应该使用这些标准和标记集。但是大多数情况下,还是需要什么标记就创建什么标记。
以下是一些其他可能的标记:
<MOLECULE> <INTEGRAL>
<PERSON> <SALARY>
<author> <email>
<planet> <sign>
<Bill> <plus/>
<Hillary> <plus/>
<Gennifer> <plus/>
<Paula> <plus/>
<Monica> <equals/>
<divorce>
可以与标记相联系的第三类意义是样式意义。样式意义指定标记的内容如何在计算机屏幕上或是其他输出设备上展示。样式意义说明特定的元素是否是用粗体、斜体、绿色的24磅的字体还是其他字体加以表示。计算机在理解样式时比理解语义意义要好一些。在XML中,样式意义是通过样式单来施加的。
XML允许用户来创建任何所需要的标记。当然,由于用户在创建标记上有完全的自由,因而通用的浏览器无法预期用户的标记的意义,也无法为显示这些标记而提供规则。因而,用户必须为文档编写样式单,告诉浏览器如何显示特定的标记。与标记集类似,用户创建的样式单可由不同的文档不同的人所共享,还可将自己创建的样式单与其他人编写的样式单集成在一起。
正如在第1章中所讨论的,现在有不止一种样式单语言可以使用。这里所用的是级联样式单(Cascading Style Sheets,简写为CSS)。CSS的优势在于它是W3C制定的标准,为编写HTML的许多人所熟悉,且被前卫的具有XML能力的浏览器所支持。
正如在第1章所注意到的,另一种可能的选择是可扩展的样式语言(Extensible Style Language)。XSL是当前最强大和灵活的样式语言,是特别为应用XML而设计的。但是,XSL比CSS更为复杂,而且未被很好地支持,同时还没有完成。
XSL将在第5、14和15章中加以讨论。
清单3-2中的greeting.xml示例只包括一个标记<GREETING>,因而所需做的一切是为GREETING元素定义样式。清单3-5是一个很简单的样式单,指定GREETING元素的内容应该以24磅的粗体显示为块级的元素。
清单3-5:greeting.xsl
GREETING{display: block; font-size: 24pt; font-weight: bold;}
清单3-5应该在文本编辑器中键入,保存为名为greeting.css的新文件,放在与清单3-2中的文件所在的同一目录中。扩展名.css代表级联样式单(Cascading Style Sheet)。同样.css扩展名是重要的,而文件名却不怎么重要。如果打算将这一样式单只用在一个XML文档上的话,那么与XML具有同样的文件名(扩展名为.css而不是.xml)常常更为方便。
在编写好XML文档和用于该文档的CSS样式单之后,还需要告诉浏览器将样式单作用到该文档上。长时期以来,可能有许多不同的方法可达到这一目的,包括浏览器-服务器通过HTTP文件头协商、命名约定和浏览器一侧的缺省方法。但是目前,唯一的有效方法是在XML文档中包括另一个处理指令,以便指定所要使用的样式单。
处理指令是<?xml-stylesheet?>和它的两个特性,type和href。type特性指定所用的样式语言,而href特性指定一个可以找到样式单的URL(可能是相对的)。在清单3-6中,xml-stylesheet处理指令指明施加于文档的样式单文件名为greeting.css,是用CSS样式单语言编写的。
清单3-6:带有xml样式单处理指令的greeting.xml
<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/css2" href="greeting.css"?>
<GREETING>
Hello XML!
</GREETING>
既然我们已经创建好了第一个XML文档和样式单,那么当然想看一看结果了。我们所要做的就是将清单3-6装入Mozilla或是Internet Explorer 5.0。图3-3是显示在Internet Explorer 5.0中的具有样式的欢迎画面 。图3-4是显示在早期开发版本的Mozilla中的具有样式的欢迎画面。

图3-3 在Internet Explorer 5.0中显示的styledgreeting.xml文件

图3-4 在早期的开发者版本的Mozilla中显示的styledgreeting.xml文件
在本章中,读者学到了如何创建一个简单的XML文档。总的来说,包括以下内容:
· 如何编写和保存简单的XML文档
在下一章中,我们将要研究XML文档的更为大型的例子,用来演示在选择XML标记时的更多的实际考虑。
在本章中,我们将要研究一个较长的示例,用来说明一个较长的有关棒球统计和其他类似数据的列表是如何以XML格式保存的。像这样的文档有好多潜在的应用。最明显的,它可以显示在Web 页面上。还可以用作其他分析数据或是整理数据程序的输入。通过这个示例,读者将学到如何用XML来标记数据、为什么要选用XML标记、如何为文档编制CSS样式单等等内容。
本章的主要内容包括:
· 检查数据
当我写作这本书时(1998年10月),纽约的Yankees队在四场比赛中击败圣·迭格的Padres队,取得了24届世界系列赛的冠军。Yankees队在American League的普通赛季结束时,取得了114场胜利。总体来说,1998是一个令人赞叹的赛季。圣·路易斯Cardinals队的 Mark McGwire和芝加哥Cubs队的Sammy Sosa为了创造新的单一赛季的本垒打纪录在整个9月份展开了争夺,原来的纪录是由Roger Maris保持的。
是什么使1998赛季这样激动人心呢?玩世不恭的人会告诉你,1998是一扩展年,有三个新队加盟,因而总体上来说投手能力减弱了。这就使得著名的击球手如Sosa和McGwire以及著名的球队,如Yankees得到了出风头的机会,因为,虽然他们仍然像他们在1997年一样实力强大,但面对的对手的平均能力弱了许多。当然真正的棒球爱好者了解真正的原因,这是由于统计上的原因造成的。
这实在有点滑稽。在大多数体育项目中,我们都说过心脏、勇气、能力、技巧、决心和其他名词。但是,只有棒球爱好者需要面对这么多原始数字,如平均击球率、平均得分、平均跑垒数、平均进垒数、对左手投手的平均击球率、对右手投手的平均击球率等。
棒球爱好者都被这些数字所迷住了,数字越多越好。在每个赛季中,因特网成了成千上万的棒球爱好者的大本营,狂热的网民们在其中“管理”球队并交换球员,计算他们喜爱的球队在现实中表现的各种数字。STATS, Inc.公司跟踪了每个球员在主要联赛的赛事上的表现,因而可以计算出一个击球手是否表现得比他的平均成绩要好。在以下两节中,为了照顾对棒球不太感兴趣的读者,我们检查一下描述单个球员的击球和投球率的常用统计数字。现场统计数字也可以找到,但是我将把这些数字略去,以便将示例局限于好管理的大小。我使用的这个特殊的例子是纽约的Yankees队,对于任何队的击球手,同样的统计数字也可以得到。
几年前,Bruce Bukiet、Jose Palacios和我写过一篇名为A Markov Chain Approach to Baseball (用于棒球的马尔可夫链式方法)的文章(刊登在Operations Research(运筹学研究杂志),45卷第1期,1997年1-2月号,pp. 14-23, 还可在以下网址上看到这篇文章http://www.math.njit.edu/~bukiet/Papers /ball.pdf)。在这篇文章中,我们分析了1989年全国棒球联赛中的所有球队的所有可能的比赛顺序。那篇文章的结果还是比较有意思的。球队中的最坏的击球手(通常是投球手)应该是第8位出场击球的人,而不应该是第9位,至少在全国棒球联赛上是如此。但是这里我所关心的是产生那篇文章的工作。作为一个低年级的研究生,用手工算出每个球员在全国棒球联赛上的全部击球历史记录正是我的工作。如果我能够使那些数据变得像XML一样的方便,那个夏季我会过得更愉快一些的。现在,让我们将精力集中于每个球员的数据上。典型的,这种数据是以一行行的数字表示的,如表4-1所示的是1998年Yankees队的进攻队员的数据。在美国棒球联赛的比赛上,由于投球手很少击球,只有实际上击球的队员才列在表中。
每一列有效地定义了一个元素。因而就需要为球员、位置、进行的比赛、击球、跑垒 、击球数、两垒、三垒、本垒打、跑入和步行等建立元素。单垒通常都不单独报告。这个数据是从总击打数中减去双垒、三垒和本垒打的总和后得到的。
表4-1 The 1998年Yankees队的进攻队员数据
|
Name |
Postion |
Game Played |
At Bats |
Runs |
Hits |
Doubles |
Triples |
Home Runs |
Runs |
Strike |
Outs |
Hit |
|
|
|
Scott Brosius |
Third Base |
152 |
530 |
86 |
159 |
34 |
0 |
19 |
98 |
52 |
97 |
10 |
|
|
|
Homer Bush |
Second BBase |
45 |
71 |
17 |
27 |
3 |
0 |
1 |
5 |
5 |
19 |
0 |
|
|
|
Chad Curtis |
Outfield |
151 |
456 |
79 |
111 |
21 |
1 |
10 |
56 |
75 |
80 |
7 |
|
|
|
Chili Davis |
Designated Hitter |
35 |
103 |
11 |
30 |
7 |
0 |
3 |
9 |
14 |
18 |
0 |
|
|
|
Mike Figga |
catcher |
1 |
4 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
|
|
|
Joe Girardi |
catcher |
78 |
254 |
31 |
70 |
11 |
4 |
3 |
31 |
14 |
38 |
2 |
|
|
|
Derek Jeter |
Shortsho |
149 |
626 |
127 |
203 |
25 |
8 |
19 |
84 |
57 |
119 |
5 |
|
|
|
Chuck |
Second Base |
150 |
603 |
117 |
160 |
25 |
4 |
17 |
64 |
76 |
70 |
|
|
|
|
Ricky Ledee |
Outfield |
42 |
79 |
13 |
19 |
5 |
2 |
1 |
12 |
7 |
29 |
0 |
|
|
|
Mike Lowell |
Third Base |
8 |
15 |
1 |
4 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
|
|
|
Tino Martinez |
First Base |
142 |
531 |
92 |
149 |
33 |
1 |
28 |
123 |
61 |
83 |
6 |
|
|
|
Paul O’Neill |
Outfield |
152 |
602 |
95 |
191 |
40 |
2 |
24 |
116 |
57 |
103 |
2 |
|
|
|
Jorge Posada |
catcher |
111 |
358 |
56 |
96 |
23 |
0 |
17 |
63 |
47 |
92 |
0 |
|
|
|
Tim Raines |
Outfield |
109 |
321 |
53 |
93 |
13 |
1 |
5 |
47 |
55 |
49 |
3 |
|
|
|
Luis Sojo |
Shortshop |
54 |
147 |
16 |
34 |
3 |
1 |
0 |
14 |
4 |
15 |
0 |
|
|
|
Shane Spencer |
Outfield |
27 |
67 |
18 |
25 |
6 |
0 |
10 |
27 |
5 |
12 |
0 |
|
|
|
Darryl
|
Designated |
101 |
295 |
44 |
73 |
11 |
2 |
24 |
57 |
46 |
90 |
3 |
|
|
|
Dale Sveum |
First Base |
30 |
58 |
6 |
9 |
0 |
0 |
0 |
3 |
4 |
16 |
0 |
|
|
|
Bernie Williams |
Outfield |
128 |
499 |
101 |
169 |
30 |
5 |
26 |
97 |
74 |
81 |
1 |
|
|
译者注:棒球数据不过是一种演示。在棒球统计数据的XML文档中,由于使用的是英文专用名词,故这里未翻译成中文。如果翻译过来反而无法相互对照。表4-2也同样处理。
前面表中的数据和下一节中的投球手数据都是加以限制后的列表,只是用来表明在一个典型的棒球赛中收集的数据。除了列出的以外,还有许多其他数据没有在这里列出。我打算使用这些基本信息,以便使示例容易管理。
人们并不指望投球手成为全垒跑的击球手或是偷袭能手。确实偶尔到达第一垒的投球手是对一个队的意外奖励。对投球手的评价要根据表4-2中列出的全场的不同种类的数字。这个表的每列也定义了一个元素。这些元素中的一部分,如姓名和位置对于投球手和击球手都是有的。其他元素如解救(saves)和成功防守(shutouts)只适用于投球手。还有几个,如得分(runs)和全垒跑(home runs)与击球手统计中的名称相同,但是具有不同意义。例如,击球手的得分数是击球手获得的分数。而对于投球手来说,是指对方在这个投球手下得到的分数。
XML是建立在容器模型的基础之上的。每个XML元素可以包含文本或是称为子元素的其他XML元素。有几个XML元素既可以包含文本也可以包含子元素。虽然通常来说,这并不是一种好形式,是应该尽量避免的。
不过,常常有不止一种组织数据的方法,这要取决于需要。XML的一个好处是,它使得编写程序来以不同形式组织数据变得相当直接。在第14章我们讨论XSL变换时还要讨论这一问题。
作为开始,必须注意的第一个问题是什么包含什么?例如,相当明显的是,联赛包含分部,分部包含球队,球队又包含球员,而球员又可在指定的时间进行交易,每个球员必定属于一个球队,每个球队又必定属于一个分部。类似的,一个赛季包含许多场比赛,每场比赛又包含几局,而局又包含击球阶段,击球阶段又包含投球阶段。
但是,赛季包括联赛吗或是联赛包括赛季吗?这个问题就不是很明显。确实对这样的问题没有唯一的答案。将赛季元素定义为联赛元素的子元素还是将联赛元素变为赛季元素的子元素有更多的意义,这要依赖于数据要用来干什么。用户甚至可以创建新的既包含赛季也包含联赛的根元素,哪个元素也不是另外元素的子元素(虽然要有效地这样做,还需要某些先进的技术,在以下几章还讨论不到这些技术)。用户可按用户的意愿来组织数据。
表4-2 1998年Yankees队的投球手
|
Name |
P |
W |
L |
S |
G |
GS |
CG |
SHO |
ERA |
IP |
H |
HR |
R |
ER |
HB |
WP |
BK |
WB |
SO |
|
Joe Borowski |
Relief Pitcher |
1 |
0 |
0 |
8 |
0 |
0 |
0 |
6.52 |
9.2 |
11 |
0 |
7 |
7 |
0 |
0 |
0 |
4 |
7 |
|
Ryan Bradley |
Relief Pitcher |
2 |
1 |
0 |
5 |
1 |
0 |
0 |
5.68 |
12.2 |
12 |
2 |
9 |
8 |
1 |
0 |
0 |
9 |
13 |
|
Jim Bruske |
Relief Pitcher |
1 |
0 |
0 |
3 |
1 |
0 |
0 |
3 |
9 |
9 |
2 |
3 |
3 |
0 |
0 |
0 |
1 |
3 |
|
Mike Buddie |
Relief Pitcher |
4 |
1 |
0 |
24 |
2 |
0 |
0 |
5.62 |
41.2 |
46 |
5 |
29 |
26 |
3 |
2 |
1 |
13 |
20 |
|
David Cone |
Starting Pitcher |
20 |
7 |
0 |
31 |
31 |
3 |
0 |
3.55 |
207.2 |
186 |
20 |
89 |
82 |
15 |
6 |
0 |
59 |
209 |
|
Todd Erdos |
Relief Pitcher |
0 |
0 |
0 |
2 |
0 |
0 |
0 |
9 |
2 |
5 |
0 |
2 |
2 |
0 |
0 |
0 |
1 |
0 |
|
Orlando Hernandez |
Starting Pitcher |
12 |
4 |
0 |
21 |
21 |
3 |
1 |
3.13 |
141 |
113 |
11 |
53 |
49 |
6 |
5 |
2 |
52 |
131 |
|
Darren Holmes |
Relief Pitcher |
0 |
3 |
2 |
34 |
0 |
0 |
0 |
3.33 |
51.1 |
53 |
4 |
19 |
19 |
2 |
1 |
0 |
14 |
31 |
|
Hideki Irabu |
Starting Pitcher |
13 |
9 |
0 |
29 |
28 |
2 |
1 |
4.06 |
173 |
148 |
27 |
79 |
78 |
9 |
6 |
1 |
76 |
126 |
|
Mike Jerzembeck |
Starting Pitcher |
0 |
1 |
0 |
3 |
2 |
0 |
0 |
12.79 |
6.1 |
9 |
2 |
9 |
9 |
0 |
1 |
1 |
4 |
1 |
|
Graeme Lloyd |
Relief Pitcher |
3 |
0 |
0 |
50 |
0 |
0 |
0 |
1.67 |
37.2 |
26 |
3 |
10 |
7 |
2 |
2 |
0 |
6 |
20 |
|
Ramiro Mendoza |
Relief Pitcher |
10 |
2 |
1 |
41 |
14 |
1 |
1 |
3.25 |
130.1 |
131 |
9 |
50 |
47 |
9 |
3 |
0 |
30 |
56 |
|
Jeff Nelson |
Relief Pitcher |
5 |
3 |
3 |
45 |
0 |
0 |
0 |
3.79 |
40.1 |
44 |
1 |
18 |
17 |
8 |
2 |
0 |
22 |
35 |
|
Andy Pettitte |
Starting Pitcher |
16 |
11 |
0 |
33 |
32 |
5 |
0 |
4.24 |
216.1 |
226 |
20 |
10 |
2 |
6 |
5 |
0 |
87 |
146 |
|
Mariano Rivera |
Relief Pitcher |
3 |
0 |
36 |
54 |
0 |
0 |
0 |
1.91 |
61.1 |
48 |
3 |
13 |
13 |
1 |
0 |
0 |
17 |
36 |
|
Mike Stanton |
Relief Pitcher |
4 |
1 |
6 |
67 |
0 |
0 |
0 |
5.47 |
79 |
71 |
13 |
51 |
48 |
4 |
0 |
0 |
26 |
69 |
|
Jay Tessmer |
Relief Pitcher |
1 |
0 |
0 |
7 |
0 |
0 |
0 |
3.12 |
8.2 |
4 |
1 |
3 |
3 |
0 |
1 |
0 |
4 |
6 |
|
David Wells |
Starting Pitcher |
18 |
4 |
0 |
30 |
30 |
8 |
5 |
3.49 |
214.1 |
195 |
29 |
86 |
83 |
1 |
2 |
0 |
29 |
163 |
熟悉数据库理论的读者可能会将XML模型看作为分支型的数据库,因而也就认为与分支数据库具有同样的缺点(和少数优点)。许多时候以表为基础的关系型方法更有实际意义。在本例中,也属于有实际意义的情况。但是,XML并不遵循关系模型。
让我们用XML处理1998年的Major League赛季数据的标记开始。请记住,在XML内,允许我们创建标记。我们已经决定,文档的根元素是赛季(season)。赛季包括联赛(leagues),而联赛包括分部(divisions),分部又包括球队(teams),球队包括队员(players)。队员的统计数字包括参加的场数(games played)、击球次数(at bats)、得分数(runs)、击中数(hits)、双垒(doubles)、三垒(triples)、全垒得分(home runs)、击球得分(runs batted in)、走步数(walks)和被投手击中数(hits by pitch)。
XML文档可由XML声明加以识别。这是放在所有XML文档的开头的一条处理指令,标识正在使用的XML版本。当前可理解的唯一版本号是1.0。
<?xml version="1.0"?>
每个合格的XML文档(所谓合格有特定的意义,这将在下一章中加以讨论)必须有一个根元素。这是一个完全包括文档中其他所有元素的元素。根元素的起始标记要放在所有其他元素的起始标记之前,而根元素的结束标记要放在所有其他元素的结束标记之后。对于我们的根元素SEASON,其起始标记是<SEASON>,而结束标记是</SEASON>。文档现在看起来像下面的样子:
<?xml version="1.0"?>
<SEASON>
</SEASON>
XML声明既不是元素也不是标记。它是处理指令。因而不需要将声明放在根元素SEASON之内。但是,我们在文档中放入的每个元素都得放在起始标记<SEASON>和结束标记</SEASON>之间。
根元素的这种选择方法说明我们已经不能在一个文件中保存多个赛季的数据了。如果想要保存多个赛季的数据的话,可以定义一个新的包括赛季(seasons)的根元素,例如,
<?xml version="1.0"?>
<DOCUMENT>
<SEASON>
</SEASON>
<SEASON>
</SEASON>
</DOCUMENT>
命名约定
在开始之前,我还要说几句关于命名约定的话。正如我们在下一章中所见到的,XML的元素名是比较灵活的,可以包括任意数目的字母和数字,既可是大写的也可是小写的。可以将XML标记写成下面的任何样子:
<SEASON>
<Season>
<season>
<season1998>
<Season98>
<season_98>
这就会有成千上万种可能的变化。全使用大写、全使用小写或是混合大小写都是可以的。但是,我推荐使用一种约定,并坚持下去。
当然,我们对所谈到的赛季加以标识。为达此目的,可为SEASON元素定义一个名为YEAR的子元素。例如:
<?xml version="1.0"?>
<SEASON>
<YEAR>
1998
</YEAR>
</SEASON>
我在此处以及其他例子中使用了缩进,以便指明元素YEAR是元素SEASON的子元素,而文本1998是元素YEAR的内容。这是一种很好的编程习惯,但这不是必须的。XML中的空白没有特殊的意义。同样的例子也可写成下面的样子:
<?xml version="1.0"?>
<SEASON>
<YEAR>1998</YEAR>
</SEASON>
确实,我经常将元素压缩到一行上(当一行上可以放得下,而空间又比较紧张时)。还可以将文档再加以压缩,即使压缩成一行也可以,但这要失去可读性。例如:
<?xml version="1.0"?><SEASON><YEAR>1998</YEAR></SEASON>
当然这样的文档是比较难以阅读和理解的,这也就是为什么我没有这样书写的原因。XML 1.0规范中的第十条目的中写道:“Terseness in XML markup is of minimal importance.”翻译成中文是,“XML标记中的简捷性是不太重要的。”棒球示例完全反映出了这个目的。
主要棒球联赛分成两个联赛:American League和National League。每个联赛都有名称。两个名称可如下编码:
<?xml version="1.0"?>
<SEASON>
<YEAR>1998</YEAR>
<LEAGUE>
<LEAGUE_NAME>National League</LEAGUE_NAME>
</LEAGUE>
<LEAGUE>
<LEAGUE_NAME>American League</LEAGUE_NAME>
</LEAGUE>
</SEASON>
我在这里将联赛的名称定义为元素LEAGUE_NAME,而不是简单的NAME元素。因为NAME太普遍了,而且还打算将其用在其他场合。例如,分部、球队和球员都有名称。
带有相同的名称的不同领域的元素可以利用命名域(namespaces)结合在一起。命名域的问题将在第18章中加以讨论。但是,即使使用命名域,也不要将同一领域(如本例中的TEAM和LEAGUE)的多个术语给予同样的名称。
每个联赛可分为东部(east)、西部(west)和中部(central)分部,可编码如下:
<LEAGUE>
<LEAGUE_NAME>National League</LEAGUE_NAME>
<DIVISION>
<DIVISION_NAME>East</DIVISION_NAME>
</DIVISION>
<DIVISION>
<DIVISION_NAME>Central</DIVISION_NAME>
</DIVISION>
<DIVISION>
<DIVISION_NAME>West</DIVISION_NAME>
</DIVISION>
</LEAGUE>
<LEAGUE>
<LEAGUE_NAME>American League</LEAGUE_NAME>
<DIVISION>
<DIVISION_NAME>East</DIVISION_NAME>
</DIVISION>
<DIVISION>
<DIVISION_NAME>Central</DIVISION_NAME>
</DIVISION>
<DIVISION>
<DIVISION_NAME>West</DIVISION_NAME>
</DIVISION>
</LEAGUE>
元素的实际值依赖于包括该元素的父元素。American League和National League都有East分部,但是这不是一回事。
每个分部又分为多个球队。每个球队都有一个队名和城市名。例如,与American League联赛East分部有关的名称可编码如下:
<DIVISION>
<DIVISION_NAME>East</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Baltimore</TEAM_CITY>
<TEAM_NAME>Orioles</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Boston</TEAM_CITY>
<TEAM_NAME>Red Sox</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>New York</TEAM_CITY>
<TEAM_NAME>Yankees</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Tampa Bay</TEAM_CITY>
<TEAM_NAME>Devil Rays</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Toronto</TEAM_CITY>
<TEAM_NAME>Blue Jays</TEAM_NAME>
</TEAM>
</DIVISION>
每个球队是由球员组成的。每个球员都有姓和名。将姓和名分开是重要的,这样一来既可以根据名来分类也可以根据姓来分类。1998年Yankees阵容第一个出场的投球手的数据可编码如下:
<TEAM>
<TEAM_CITY>New York</TEAM_CITY>
<TEAM_NAME>Yankees</TEAM_NAME>
<PLAYER>
<GIVEN_NAME>Orlando</GIVEN_NAME>
<SURNAME>Hernandez</SURNAME>
</PLAYER>
<PLAYER>
<GIVEN_NAME>David</GIVEN_NAME>
<SURNAME>Cone</SURNAME>
</PLAYER>
<PLAYER>
<GIVEN_NAME>David</GIVEN_NAME>
<SURNAME>Wells</SURNAME>
</PLAYER>
<PLAYER>
<GIVEN_NAME>Andy</GIVEN_NAME>
<SURNAME>Pettitte</SURNAME>
</PLAYER>
<PLAYER>
<GIVEN_NAME>Hideki</GIVEN_NAME>
<SURNAME>Irabu</SURNAME>
</PLAYER>
</TEAM>
为了更明显起见,使用标记<GIVEN_NAME>和<SURNAME>比使用<FIRST_NAME>
和<LAST_NAME>或者<FIRST_NAME>和<FAMILY_NAME>更好一些。由于不同国家的文化背景不同,可能名(given
name)在先也可能姓(family name)在先。同时在所有的文化背景下,别号(surnames)不一定就是姓(family names)。
以下几个步骤提供了每个球员的统计数据。统计数据看起来对于投球手和击球手并没有一点不同,特别是对于American League联赛,这里没有几个投球员击过球。下面是Joe Girardi在1998年的统计数据。他是一个接球手,因而我们使用击球的统计数据:
<PLAYER>
<GIVEN_NAME>Joe </GIVEN_NAME>
<SURNAME>Girard </SURNAME>
<POSITION>Catcher</POSITION>
<GAMES>78</GAMES>
<GAMES_STARTED>76</GAMES_STARTED>
<AT_BATS>254</AT_BATS>
<RUNS>31</RUNS>
<HITS>70</HITS>
<DOUBLES>11</DOUBLES>
<TRIPLES>4</TRIPLES>
<HOME_RUNS>3</HOME_RUNS>
<RBI>31</RBI>
<STEALS>2</STEALS>
<CAUGHT_STEALING>4</CAUGHT_STEALING>
<SACRIFICE_HITS>8</SACRIFICE_HITS>
<SACRIFICE_FLIES>1</SACRIFICE_FLIES>
<ERRORS>3</ERRORS>
<WALKS>14</WALKS>
<STRUCK_OUT>38</STRUCK_OUT>
<HIT_BY_PITCH>2</HIT_BY_PITCH>
</PLAYER>
现在让我们看一下一个投球手的统计数据。虽然投球手在American League中很少击球,但在National League中却常常击球,到目前为止,投球手击球的次数还是比其他球员少。根据投球手的投球表现,雇用或解雇、表扬或批评。如果投球手偶尔击中一球,则会得到额外的奖励。投球的统计包括比赛场数(games played)、得胜场数(wins)、失败场数(losses)、投球局数(innings pitched)、得分(earned runs)、成功防守次数(shutouts)、击中数(hits against)、走步放弃(walks given up)和其他数据。下面是Hideki Irabu1998年统计数据的XML编码:
<PLAYER>
<GIVEN_NAME>Hideki</GIVEN_NAME>
<SURNAME>Irabu</SURNAME>
<POSITION>Start ng P tcher</POSITION>
<WINS>13</WINS>
<LOSSES>9</LOSSES>
<SAVES>0</SAVES>
<GAMES>29</GAMES>
<GAMES_STARTED>28</GAMES_STARTED>
<COMPLETE_GAMES>2</COMPLETE_GAMES>
<SHUT_OUTS> </SHUT_OUTS>
<ERA>4.06</ERA>
<INNINGS> 73</INNINGS>
<HOME_RUNS>148</HOME_RUNS>
<RUNS>27</RUNS>
<EARNED_RUNS>79</EARNED_RUNS>
<HIT_BATTER>78</HIT_BATTER>
<WILD_PITCHES>9</WILD_PITCHES>
<BALK>6</BALK>
<WALKED_BATTER>1</WALKED_BATTER>
<STRUCK_OUT_BATTER>76</STRUCK_OUT_BATTER>
</PLAYER>
XML标记的简洁性不是太重要
从整个示例来看,我已经遵循了XML的明显的原则:“Terseness in XML markup is of minimal importance.”(XML标记的简洁性不是太重要的。)这当然对非棒球文化下的读者很有帮助。这些读者对于棒球术语及其简写不是很熟悉。比如无法了解为什么 walk的简写是 BB(base on balls)而不是人们以为的W。如果文档的大小是个问题的话,将文档用Zip一类的工具进行压缩还是很容易的。
但是,这并不意味着XML是相当长的,也不意味着手工键入是非常枯燥无味的。我承认,本例强烈地吸引我使用简略语来书写,这样一来,清晰性就丧失殆尽。如果我使用简写,那么典型的PLAYER元素可能如下:
<PLAYER>
<GIVEN_NAME>Joe</GIVEN_NAME>
<SURNAME>Girard </SURNAME>
<P>C</P>
<G>78</G>
<AB>254</AB>
<R>31</R>
<H>70</H>
<DO>11</DO>
<TR>4</TR>
<HR>3</HR>
<RBI>3 </RBI>
<BB>14</BB>
<SO>38</SO>
<SB>2</SB>
<CS>4</CS>
<HBP>2</HBP>
</PLAYER>
到目前为止,我向读者展示的只是一段一段(每段一个元素)的XML文档。但现在是该将各段组装在一起,看一看包括1998年Major League赛季的统计数据的全部文档的时候了。清单4-1列出了完成了的XML文档,其中包括两个联赛、六个分部、三十个队和九个球员。
清单4-1:一份完整的XML文档
<?xml version="1.0"?>
<SEASON>
<YEAR>1998</YEAR>
<LEAGUE>
<LEAGUE_NAME>National League</LEAGUE_NAME>
<DIVISION>
<DIVISION_NAME>East</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Atlanta</TEAM_CITY>
<TEAM_NAME>Braves</TEAM_NAME>
<PLAYER>
<SURNAME>Malloy</SURNAME>
<GIVEN_NAME>Marty</GIVEN_NAME>
<POSITION>Second Base</POSITION>
<GAMES>11</GAMES>
<GAMES_STARTED>8</GAMES_STARTED>
<AT_BATS>28</AT_BATS>
<RUNS>3</RUNS>
<HITS>5</HITS>
<DOUBLES>1</DOUBLES>
<TRIPLES>0</TRIPLES>
<HOME_RUNS>1</HOME_RUNS>
<RBI>1</RBI>
<STEALS>0</STEALS>
<CAUGHT_STEALING>0</CAUGHT_STEALING>
<SACRIFICE_HITS>0</SACRIFICE_HITS>
<SACRIFICE_FLIES>0</SACRIFICE_FLIES>
<ERRORS>0</ERRORS>
<WALKS>2</WALKS>
<STRUCK_OUT>2</STRUCK_OUT>
<HIT_BY_PITCH>0</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Guillen</SURNAME>
<GIVEN_NAME>Ozzie </GIVEN_NAME>
<POSITION>Shortstop</POSITION>
<GAMES>83</GAMES>
<GAMES_STARTED>59</GAMES_STARTED>
<AT_BATS>264</AT_BATS>
<RUNS>35</RUNS>
<HITS>73</HITS>
<DOUBLES>15</DOUBLES>
<TRIPLES>1</TRIPLES>
<HOME_RUNS>1</HOME_RUNS>
<RBI>22</RBI>
<STEALS>1</STEALS>
<CAUGHT_STEALING>4</CAUGHT_STEALING>
<SACRIFICE_HITS>4</SACRIFICE_HITS>
<SACRIFICE_FLIES>2</SACRIFICE_FLIES>
<ERRORS>6</ERRORS>
<WALKS>24</WALKS>
<STRUCK_OUT>25</STRUCK_OUT>
<HIT_BY_PITCH>1</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Bautista</SURNAME>
<GIVEN_NAME>Danny</GIVEN_NAME>
<POSITION>Outfield</POSITION>
<GAMES>82</GAMES>
<GAMES_STARTED>27</GAMES_STARTED>
<AT_BATS>144</AT_BATS>
<RUNS>17</RUNS>
<HITS>36</HITS>
<DOUBLES>1</DOUBLES>
<TRIPLES>0</TRIPLES>
<HOME_RUNS>3</HOME_RUNS>
<RBI>17</RBI>
<STEALS>1</STEALS>
<CAUGHT_STEALING>0</CAUGHT_STEALING>
<SACRIFICE_HITS>3</SACRIFICE_HITS>
<SACRIFICE_FLIES>2</SACRIFICE_FLIES>
<ERRORS>2</ERRORS>
<WALKS>7</WALKS>
<STRUCK_OUT>21</STRUCK_OUT>
<HIT_BY_PITCH>0</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Williams</SURNAME>
<GIVEN_NAME>Gerald</GIVEN_NAME>
<POSITION>Outfield</POSITION>
<GAMES>129</GAMES>
<GAMES_STARTED>51</GAMES_STARTED>
<AT_BATS>266</AT_BATS>
<RUNS>46</RUNS>
<HITS>81</HITS>
<DOUBLES>18</DOUBLES>
<TRIPLES>3</TRIPLES>
<HOME_RUNS>10</HOME_RUNS>
<RBI>44</RBI>
<STEALS>1</STEALS>
<CAUGHT_STEALING>5</CAUGHT_STEALING>
<SACRIFICE_HITS>2</SACRIFICE_HITS>
<SACRIFICE_FLIES>1</SACRIFICE_FLIES>
<ERRORS>5</ERRORS>
<WALKS>17</WALKS>
<STRUCK_OUT>48</STRUCK_OUT>
<HIT_BY_PITCH>3</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Glavine</SURNAME>
<GIVEN_NAME>Tom</GIVEN_NAME>
<POSITION>Starting Pitcher</POSITION>
<WINS>20</WINS>
<LOSSES>6</LOSSES>
<SAVES>0</SAVES>
<GAMES>33</GAMES>
<GAMES_STARTED>33</GAMES_STARTED>
<COMPLETE_GAMES>4</COMPLETE_GAMES>
<SHUT_OUTS>3</SHUT_OUTS>
<ERA>2.47</ERA>
<INNINGS>229.1</INNINGS>
<HOME_RUNS>202</HOME_RUNS>
<RUNS>13</RUNS>
<EARNED_RUNS>67</EARNED_RUNS>
<HIT_BATTER>63</HIT_BATTER>
<WILD_PITCHES>2</WILD_PITCHES>
<BALK>3</BALK>
<WALKED_BATTER>0</WALKED_BATTER>
<STRUCK_OUT_BATTER>74</STRUCK_OUT_BATTER>
</PLAYER>
<PLAYER>
<SURNAME>Lopez</SURNAME>
<GIVEN_NAME>Javier</GIVEN_NAME>
<POSITION>Catcher</POSITION>
<GAMES>133</GAMES>
<GAMES_STARTED>124</GAMES_STARTED>
<AT_BATS>489</AT_BATS>
<RUNS>73</RUNS>
<HITS>139</HITS>
<DOUBLES>21</DOUBLES>
<TRIPLES>1</TRIPLES>
<HOME_RUNS>34</HOME_RUNS>
<RBI>106</RBI>
<STEALS>5</STEALS>
<CAUGHT_STEALING>3</CAUGHT_STEALING>
<SACRIFICE_HITS>1</SACRIFICE_HITS>
<SACRIFICE_FLIES>8</SACRIFICE_FLIES>
<ERRORS>5</ERRORS>
<WALKS>30</WALKS>
<STRUCK_OUT>85</STRUCK_OUT>
<HIT_BY_PITCH>6</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Klesko</SURNAME>
<GIVEN_NAME>Ryan</GIVEN_NAME>
<POSITION>Outfield</POSITION>
<GAMES>129</GAMES>
<GAMES_STARTED>124</GAMES_STARTED>
<AT_BATS>427</AT_BATS>
<RUNS>69</RUNS>
<HITS>17</HITS>
<DOUBLES>29</DOUBLES>
<TRIPLES>1</TRIPLES>
<HOME_RUNS>18</HOME_RUNS>
<RBI>70</RBI>
<STEALS>5</STEALS>
<CAUGHT_STEALING>3</CAUGHT_STEALING>
<SACRIFICE_HITS>0</SACRIFICE_HITS>
<SACRIFICE_FLIES>4</SACRIFICE_FLIES>
<ERRORS>2</ERRORS>
<WALKS>56</WALKS>
<STRUCK_OUT>66</STRUCK_OUT>
<HIT_BY_PITCH>3</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Galarraga</SURNAME>
<GIVEN_NAME>Andres</GIVEN_NAME>
<POSITION>First Base</POSITION>
<GAMES>153</GAMES>
<GAMES_STARTED>151</GAMES_STARTED>
<AT_BATS>555</AT_BATS>
<RUNS>103</RUNS>
<HITS>169</HITS>
<DOUBLES>27</DOUBLES>
<TRIPLES>1</TRIPLES>
<HOME_RUNS>44</HOME_RUNS>
<RBI>121</RBI>
<STEALS>7</STEALS>
<CAUGHT_STEALING>6</CAUGHT_STEALING>
<SACRIFICE_HITS>0</SACRIFICE_HITS>
<SACRIFICE_FLIES>5</SACRIFICE_FLIES>
<ERRORS>1</ERRORS>
<WALKS>63</WALKS>
<STRUCK_OUT>146</STRUCK_OUT>
<HIT_BY_PITCH>25</HIT_BY_PITCH>
</PLAYER>
<PLAYER>
<SURNAME>Helms</SURNAME>
<GIVEN_NAME>Wes</GIVEN_NAME>
<POSITION>Third Base</POSITION>
<GAMES>7</GAMES>
<GAMES_STARTED>2</GAMES_STARTED>
<AT_BATS>13</AT_BATS>
<RUNS>2</RUNS>
<HITS>4</HITS>
<DOUBLES>1</DOUBLES>
<TRIPLES>0</TRIPLES>
<HOME_RUNS>1</HOME_RUNS>
<RBI>2</RBI>
<STEALS>0</STEALS>
<CAUGHT_STEALING>0</CAUGHT_STEALING>
<SACRIFICE_HITS>0</SACRIFICE_HITS>
<SACRIFICE_FLIES>0</SACRIFICE_FLIES>
<ERRORS>1</ERRORS>
<WALKS>0</WALKS>
<STRUCK_OUT>4</STRUCK_OUT>
<HIT_BY_PITCH>0</HIT_BY_PITCH></PLAYER>
</TEAM>
<TEAM>
<TEAM_CITY>Florida</TEAM_CITY>
<TEAM_NAME>Marlins</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Montreal</TEAM_CITY>
<TEAM_NAME>Expos</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>New York</TEAM_CITY>
<TEAM_NAME>Mets</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Philadelphia</TEAM_CITY>
<TEAM_NAME>Phillies</TEAM_NAME>
</TEAM>
</DIVISION>
<DIVISION>
<DIVISION_NAME>Central</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Chicago</TEAM_CITY>
<TEAM_NAME>Cubs</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Cincinatti</TEAM_CITY>
<TEAM_NAME>Reds</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Houston</TEAM_CITY>
<TEAM_NAME>Astros</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Milwaukee</TEAM_CITY>
<TEAM_NAME>Brewers</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Pittsburgh</TEAM_CITY>
<TEAM_NAME>Pirates</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>St. Louis</TEAM_CITY>
<TEAM_NAME>Cardinals</TEAM_NAME>
</TEAM>
</DIVISION>
<DIVISION>
<DIVISION_NAME>West</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Arizona</TEAM_CITY>
<TEAM_NAME>Diamondbacks</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Colorado</TEAM_CITY>
<TEAM_NAME>Rockies</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Los Angeles</TEAM_CITY>
<TEAM_NAME>Dodgers</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>San Diego</TEAM_CITY>
<TEAM_NAME>Padres</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>San Francisco</TEAM_CITY>
<TEAM_NAME>Giants</TEAM_NAME>
</TEAM>
</DIVISION>
</LEAGUE>
<LEAGUE>
<LEAGUE_NAME>American League</LEAGUE_NAME>
<DIVISION>
<DIVISION_NAME>East</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Baltimore</TEAM_CITY>
<TEAM_NAME>Orioles</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Boston</TEAM_CITY>
<TEAM_NAME>Red Sox</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>New York</TEAM_CITY>
<TEAM_NAME>Yankees</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Tampa Bay</TEAM_CITY>
<TEAM_NAME>Devil Rays</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Toronto</TEAM_CITY>
<TEAM_NAME>Blue Jays</TEAM_NAME>
</TEAM>
</DIVISION>
<DIVISION>
<DIVISION_NAME>Central</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Chicago</TEAM_CITY>
<TEAM_NAME>White Sox</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Kansas City</TEAM_CITY>
<TEAM_NAME>Royals</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Detroit</TEAM_CITY>
<TEAM_NAME>Tigers</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Cleveland</TEAM_CITY>
<TEAM_NAME>Indians</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Minnesota</TEAM_CITY>
<TEAM_NAME>Twins</TEAM_NAME>
</TEAM>
</DIVISION>
<DIVISION>
<DIVISION_NAME>West</DIVISION_NAME>
<TEAM>
<TEAM_CITY>Anaheim</TEAM_CITY>
<TEAM_NAME>Angels</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Oakland</TEAM_CITY>
<TEAM_NAME>Athletics</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Seattle</TEAM_CITY>
<TEAM_NAME>Mariners</TEAM_NAME>
</TEAM>
<TEAM>
<TEAM_CITY>Texas</TEAM_CITY>
<TEAM_NAME>Rangers</TEAM_NAME>
</TEAM>
</DIVISION>
</LEAGUE>
</SEASON>
图4-1显示的是将本文档装入Internet Explorer 5.0的情况。

图4-1 在Internet Explorer 5.0中显示的1998年主要联赛的统计数据
即使现在这个文档也是不完全的。此文档只包括一个队的球员(Atlanta Braves队)而且只有该球队的九个球员。如果将全部都写出来的话,则示例就会变得太长,以至于本书无法将其包括。
在名为1998statistics.xml的更为完整的XML文档中,包括了1998年度两大联赛的所有球员的统计数据,这个文档附在本书光盘中,目录为examples/base-ball。同时,我故意将所包括的数据加以限制,以便符合本书的篇幅。实际上,可以包括更为详细的数据。我已经间接提到可按比赛场次、投球次数等来安排数据的可能性。即使没有那样做,还是有许多细节可以添加到每个元素中。球队还有教练、经理、老板(说到Yankees队怎能不提到George
Steinbrenner呢?)、室内运动场和其他项目。
我还故意忽略了可以从这里给出的其他项目中计算出来的数字,如平均击球数等。不管如何,球员还有许多其他数据,如臂长、身高、出生日期等。当然球员远不止这里列出的几个。所有这一切都是很容易加进XML文档的。但是我们的XML化就到此为止了,这样我们才能往下进行,首先要简短地讨论一下为什么这一数据格式是有用的,然后再讨论在Web浏览器上实际显示该文档所用的技术。
表4-1对于显示一个球队的击球数据是简捷且易于理解的。我们从改写成的简单4-1中的长得多的形式中会得到什么好处呢?好处有如下几种:
· 数据是自说明的
XML格式的第一条主要好处是数据是自描述的。每个数字的意义是清楚的,且不会错误地与数字本身相联系。当读取文档时,用户了解<HITS>
2 </HITS>中的2指的是击中数而不是得分或是防守。如果键入文档的打字员漏掉了一个数字,不会造成其后的数字都错了位。HITS就是HITS,即使它前面的RUNS元素丢失也没关系。
在本书第二部分中,读者会看到,XML还可以使用DTD来加强限制,使得某些元素,如HITS或RUNS必须存在。
第二条好处是XML提供的数据可用广泛的具有XML处理能力的工具加以处理,从相当贵的软件,如Adobe FrameMaker 到免费软件,如Python和Perl。数据量可以很大,但是数据额外的冗余就允许使用更多的工具来处理它。
当查看数据时,也同样有这样的问题。XML文档可装入Internet Explorer 5.0、Mozilla、FrameMaker 5.5.6和许多其他工具,所有这些工具都提供唯一的、有用的一种数据的视图。数据还可以装入简单的文本编辑器中,如vi、BBEdit和TextPad。这就使得数据或多或少的可在多种平台上查看。
使用新软件也不是获得数据的不同视图的唯一方法。在下一节中,我们将为棒球统计数据创建一个样式单,来提供一种与图4-1完全不同的查看数据的方法。每当对同一文档施加不同的样式单,都可以看到不同图景。
最后,要向自己发问,文件大小真是很成问题吗?当前硬盘容量已经相当大了,可以存入大量数据,即使存储得不太节省也没有太大的关系。同时,XML文件的压缩率很大。全部的两大棒球联赛1998年统计数据的文档是653K。如果用gzip 压缩一下的话,只有66K,几乎压缩了90%。先进的HTTP服务器,如Jigsaw可以发送压缩文件,而不必解压缩,因而文档所用的网络带宽与其实际信息内容已相当接近。最后,我们不能认为二进制文件格式(特别通用的格式)必定是高效的。包含1998statistics.xml文件同样数据的Microsoft Excel文件的大小达到了2.37MB,比XML格式大了三倍多。虽然我们能够创建更为有效的文件格式和编码方法,但实际上简单并不是必须的。
图4-1中的XML文档的原始视图对于某些应用来说也是不错的。例如,此视图允许折叠和展开单个的元素,因而可以只看文档中要看的部分。但大多数时候,人们总希望看到更好的形式,特别是,想要在Web上显示数据时。为了提供更好的外观,必须为文档编写样式单。
在本章中,我们使用的是CSS样式单。CSS样式单将特定的格式化信息与文档中的每个元素联系起来。我们的XML文档中使用的元素的完全列表如下:
SEASON
YEAR
LEAGUE
LEAGUE_NAME
DIVISION
DIVISION_NAME
TEAM
TEAM_CITY
TEAM_NAME
PLAYER
SURNAME
GIVEN_NAME
POSITION
GAMES
GAMES_STARTED
AT_BATS
RUNS
HITS
DOUBLES
TRIPLES
HOME_RUNS
RBI
STEALS
CAUGHT_STEALING
SACRIFICE_HITS
SACRIFICE_FLIES
ERRORS
WALKS
STRUCK_OUT
HIT_BY_PITCH
一般来说,我们要用重复的过程来为每个元素增加样式规则,一次一个元素地进行,然后检查是否达到了要求,再处理下一个元素。在本例中,这种办法对于不熟悉样式单属性的人来说也有好处。
样式单的名称可随便取。如果只是为一个文档编制样式单,那么习惯上样式单的文件与文档的文件名一样,但是三字母的扩展名是.css而不是.xml。例如,对于XML文档1998shortstats.xml来说,样式单文件可以叫做1998shortstats.css。另一方面,如果同样的样式单还要用于许多文档,那么,可能需要更为普通的文件名,如baseballstats.css。
由于CSS样式单是级联的,同一文档可有不止一个样式单。因而baseballstats.css可向文档施加某些一般的样式规则,而1998shortstats.css可覆盖其中的几条规则,以便在同一文档(1998shortstats.xml)中处理特定的细节。我们将第12章“级联样式单(级别1)”中讨论这一问题。
为了将样式单与文档联系起来,只要像下面所示简单地在XML声明和根元素间增加一个<?xml-stylesheet?>处理指令就可以了:
<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/css" href="baseballstats.css"?>
<SEASON>
...
这条指令告诉浏览器读取文档并施加保存在文件baseballstats.css中的样式单。这个文件是假设放在与XML文件同一服务器上的同一目录中的。换句话说,baseballstats.css是个相对的URL。完全的URL也是可以使用的。例如:
<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/css"
href="http://metalab.unc.edu/xml/examples/baseballstats.css"?>
<SEASON>
...
开始时,用户可以简单地将一个名为baseballstats.css的空文件放在与XML文档相同的目录中。然后向1998shortstats.xml (清单4-1)中增加适当的指令,该文档现在在浏览器中的外观如图4-2所示。只显示了元素内容。可折叠的大纲视图(图4-1)不见了。元素内容的格式使用的是浏览器的缺省格式,在本例中是黑色12磅的Times Roman 字体放在白色背景上。

图4-2 使用了空白样式之后的1998年两大棒球联赛的统计数字显示
如果在指定位置找不到样式单处理指令(xml-stylesheet)中指定的样式单文件名,也可看到一个很像图4-2的视图。
用户不必为每个元素指定样式规则。许多元素允许将其父元素的样式串接下来。因而最重要的样式是根元素的样式,在本例中就是SEASON元素。这个样式定义了页面上所有其他元素的缺省样式。大致为72 dpi的分辨率的计算机显示器不如纸上300dpi或更大的分辨率那样高。所以,Web页面通常应该使用较大磅数的字号。首先将缺省样式定义为白色背景上的14磅黑色字,定义如下:
SEASON {font-size: 14pt; background-color: white;
color: black; display: block}
将这条语句放在一个文本文件中,将其以文件名baseballstats.css与清单4-1中的文件(1998shortstats.xml)保存在同一目录中。在浏览器中打开1998shortstats.xml。我们就会看到如图4-3所示的情况。
在图4-2和图4-3之间字号发生了变化,但文本颜色和背景颜色没有变化。其实这没有必要加以设置,因为黑色文本和白色背景是缺省的。但明确地加以设置也没有损失什么。

图4-3 以14磅白地黑字显示的棒球统计数据
元素YEAR或多或少可算是文档的标题。因而使其显示得大一些,用32磅的字号也就足够大了。同时,它还应该从文档的其余部分突出出来,而不是简单地与其他内容混在一起。利用下面的样式规则可以达到这些目的:
YEAR {display: block; font-size: 32pt; font-weight: bold;
text-align: center}
图4-4显示的是将此规则增加到样式单中之后的文档。请特别注意,在“1998”后面的换行。有这个换行是由于YEAR是块级元素。而在文档中的其他元素都是内联元素。我们只能使块级元素居中(或左对齐、右对齐或两端对齐)。

图4-4 将YEAR元素格式化为标题
在使用了这种样式单的文档中,YEAR元素与HTML中的H1标题元素的功能重复了。 由于这个文档是非常整齐地分支结构,几个其他元素的功能与HTML中的H2、H3等相似。这些元素都可以用相似的规则加以格式化,只是将字号略微减小一些罢了。
例如,SEASON由两个LEAGUE元素组成。每个LEAGUE的名称,即LEAGUE_NAME元素,起了HTML中的H2元素一样的作用。每个LEAGUE元素又由三个DIVISION元素所组成。每个DIVISION的名称,也就是DIVISION_NAME元素,具有HTML中的H3元素的作用。这两条规则分别将这两种元素加以格式化:
LEAGUE_NAME {display: block; text-align: center; font-size:
28pt; font-weight: bold}
DIVISION_NAME {display: block; text-align: center; font-size:
24pt; font-weight: bold}
图4-5显示的是最后的文档。

图4-5 将LEAGUE_NAME和DIVISION_NAME元素格式化为下级标题
HTML和XML的一个重要区别是,在HTML中通常不会出现在一个元素中既包括节标题(H2、H3、H4等),又包括该节的完整内容的情况。节的内容必须包括在一级标题的结束和下一个同级标题的开始之间。这对于必须分析HTML文档的语法的软件来说是非常重要的,例如,要自动生成目录时。
Divisions又分成为TEAM元素。要将此格式化需要一些技巧,因为球队的标题并不就是TEAM_NAME元素,而是TEAM_CITY元素与TEAM_NAME拼接在一起的。所以这需要的是内联元素而不是单独的块级元素。然而,它们仍然是标题,因而我们将其设置为粗斜体的20磅字体。图4-6显示的是将这两条规则加到样式单中的结果。
TEAM_CITY {font-size: 20pt; font-weight: bold;
font-style: italic}
TEAM_NAME {font-size: 20pt; font-weight: bold;
font-style: italic}

图4-6 为队名设置样式
到此为止,将队名与城市名作为结合起来的块级元素来排列结果可能会是不错的。有几种办法可达到这个目的。例如,可以向XML文档中增加一个附加的TEAM_TITLE元素,其目的只是为了包括TEAM_NAME和TEAM_CITY。例如:
<TEAM>
<TEAM_TITLE>
<TEAM_CITY>Colorado</TEAM_CITY>
<TEAM_NAME>Rockies</TEAM_NAME>
</TEAM_TITLE>
</TEAM>
接着,可以增加一条向TEAM_TITLE施加块级格式化的样式规则:
TEAM_TITLE {display: block; text-align: center}
但是,绝不应该为了使样式单简单一些而重新排列XML文档。毕竟,样式单的总的目的是将格式化信息保存于文档之外。不过,用户可以通过别的办法达到同样的效果。其办法是,使紧挨着的上一个和下一个元素变成块级元素,也就是说,将TEAM和PLAYER变成块级元素。这就将TEAM_NAME和TEAM_CITY放在了由它们本身组成的隐式块级元素之中了。图4-7显示了其结果。
TEAM {display: block}
PLAYER {display: block}

图4-7 作为段标题而格式化的队名和城市名
本文档需要的最具技巧的格式化是对每个球员及其统计数据的格式化。每个队有几十个球员。每个球员都有统计数据。应该将TEAM元素看作是由PLAYER元素组成的,且将每个球员放在他自己的块级节中,正如前一个元素所做的那样。不过,排列这些数据的更为吸引人且更为有效的方法是使用表格。达到这一目的的样式规则如下所示:
TEAM {display: table}
TEAM_CITY {display: table-caption}
TEAM_NAME {display: table-caption}
PLAYER {display: table-row}
SURNAME {display: table-cell}
GIVEN_NAME {display: table-cell}
POSITION {display: table-cell}
GAMES {display: table-cell}
GAMES_STARTED {display: table-cell}
AT_BATS {display: table-cell}
RUNS {display: table-cell}
HITS {display: table-cell}
DOUBLES {display: table-cell}
TRIPLES {display: table-cell}
HOME_RUNS {display: table-cell}
RBI {display: table-cell}
STEALS {display: table-cell}
CAUGHT_STEALING {display: table-cell}
SACRIFICE_HITS {display: table-cell}
SACRIFICE_FLIES {display: table-cell}
ERRORS {display: table-cell}
WALKS {display: table-cell}
STRUCK_OUT {display: table-cell}
HIT_BY_PITCH {display: table-cell}
遗憾的是,只有CSS2才支持表格属性,而Internet Explorer 5.0和其他写作本书时已存在的浏览器还不支持CSS2。由于还不能使用表格的格式化方法,我们只好使TEAM和PLAYER成为块级元素,而让其他数据保持缺省格式。
清单4-2列出了完成后的样式单。CSS样式单除了一条一条的规则之外,这种样式单没有什么结构。实际上,样式单只是我在上面分别介绍过的所有规则的列表。列表中的顺序不是很重要,只要每条规则都包含进去也就可以了。
清单4-2:baseballstats.css
SEASON {font-size: 4pt; background-color: white;
color: black; display: block}
YEAR {display: block; font-size: 32pt; font-weight: bold;
text-align: center}
LEAGUE_NAME {display: block; text-align: center;
font-size: 28pt; font-weight: bold}
DIVISION_NAME {display: block; text-align: center;
font-size: 24pt; font-weight: bold}
TEAM_CITY {font-size: 20pt; font-weight: bold;
font-style: italic}
TEAM_NAME {font-size: 20pt; font-weight: bold;
font-style: italic}
TEAM {display: block}
PLAYER {display: block}
到此就完成了棒球统计数据的基本格式化的任务。不过很清楚,还有许多工作要做。支持真正表格格式化的浏览器将会大有帮助。然而还有其他工作。下面指出这些工作,其顺序没有什么关系:
· 只是列出了原始的数字,而没有说明数字代表了什么。每个数字应该有一个为其命名的标题,如“RBI”或是“At Bats”。
像这样一类的许多看法都应该向文档中增加更多的内容加以体现。例如,为了将标题从“1998”改为“1998 Major League Baseball”,所要做的工作只是将YEAR 元素改写如下:
1998 Major League Baseball
在每个花名册的顶部,用一个假想的球员名,为球员的统计数据加进小标题,如下所示:
<PLAYER>
<SURNAME>Surname</SURNAME>
<GIVEN_NAME>Given name</GIVEN_NAME>
<POSITION>Position</POSITION>
<GAMES>Games</GAMES>
<GAMES_STARTED>Games Started</GAMES_STARTED>
<AT_BATS>At Bats</AT_BATS>
<RUNS>Runs</RUNS>
<HITS>Hits</HITS>
<DOUBLES>Doubles</DOUBLES>
<TRIPLES>Triples</TRIPLES>
<HOME_RUNS>Home Runs</HOME_RUNS>
<RBI>Runs Batted In</RBI>
<STEALS>Steals</STEALS>
<CAUGHT_STEALING>Caught Stealing</CAUGHT_STEALING>
<SACRIFICE_HITS>Sacrifice Hits</SACRIFICE_HITS>
<SACRIFICE_FLIES>Sacrifice Flies</SACRIFICE_FLIES>
<ERRORS>Errors</ERRORS>
<WALKS>Walks</WALKS>
<STRUCK_OUT>Struck Out</STRUCK_OUT>
<HIT_BY_PITCH>Hit By Pitch</HIT_BY_PITCH>
</PLAYER>
关于这种方法还有一些基本问题需要解决。年份是1998年,而不是1998 Major League Baseball 。小标题“At Bats”与击球数不是一回事。(这正是事物的名称与事物本身之间的差别。)这时可增加一些标记如下(加以解决):
<TABLE_HEAD>
<COLUMN_LABEL>Surname</COLUMN_LABEL>
<COLUMN_LABEL>Given name</COLUMN_LABEL>
<COLUMN_LABEL>Position</COLUMN_LABEL>
<COLUMN_LABEL>Games</COLUMN_LABEL>
<COLUMN_LABEL>Games Started</COLUMN_LABEL>
<COLUMN_LABEL>At Bats</COLUMN_LABEL>
<COLUMN_LABEL>Runs</COLUMN_LABEL>
<COLUMN_LABEL>Hits</COLUMN_LABEL>
<COLUMN_LABEL>Doubles</COLUMN_LABEL>
<COLUMN_LABEL>Triples</COLUMN_LABEL>
<COLUMN_LABEL>Home Runs</COLUMN_LABEL>
<COLUMN_LABEL>Runs Batted In</COLUMN_LABEL>
<COLUMN_LABEL>Steals</COLUMN_LABEL>
<COLUMN_LABEL>Caught Stealing</COLUMN_LABEL>
<COLUMN_LABEL>Sacrifice Hits</COLUMN_LABEL>
<COLUMN_LABEL>Sacrifice Flies</COLUMN_LABEL>
<COLUMN_LABEL>Errors</COLUMN_LABEL>
<COLUMN_LABEL>Walks</COLUMN_LABEL>
<COLUMN_LABEL>Struck Out</COLUMN_LABEL>
<COLUMN_LABEL>Hit By Pitch</COLUMN_LABEL>
</TABLE_HEAD>
不过这样一来,基本上是重新“发明”了HTML,而且使我们又回到了使用标记来格式化而不是用于意义了。同时,我们还重复了已经包括在元素名称中的信息。整个文档还相当大,我们还是希望文档不要太大为好。
增加击球和其他的平均数并不复杂。只要将数据作为附加的元素包括进来就可以了。例如,下面是一个带有该种数据的球员:
<PLAYER>
<SURNAME>Malloy</SURNAME>
<GIVEN_NAME>Marty</GIVEN_NAME>
<POSITION>Second Base</POSITION>
<GAMES>1</GAMES>
<GAMES_STARTED>8</GAMES_STARTED>
<ON_BASE_AVERAGE>.233</ON_BASE_AVERAGE>
<SLUGGING_AVERAGE>.321</SLUGGING_AVERAGE>
<BATTING_AVERAGE>.179</BATTING_AVERAGE>
<AT_BATS>28</AT_BATS>
<RUNS>3</RUNS>
<HITS>5</HITS>
<DOUBLES>1</DOUBLES>
<TRIPLES>0</TRIPLES>
<HOME_RUNS>1</HOME_RUNS>
<RBI>1</RBI>
<STEALS>0</STEALS>
<CAUGHT_STEALING>0</CAUGHT_STEALING>
<SACRIFICE_HITS>0</SACRIFICE_HITS>
<SACRIFICE_FLIES>0</SACRIFICE_FLIES>
<ERRORS>0</ERRORS>
<WALKS>2</WALKS>
<STRUCK_OUT>2</STRUCK_OUT>
<HIT_BY_PITCH>0</HIT_BY_PITCH>
</PLAYER>
但是,这种信息是多余的,因为这些数据可从已经包括进来的数据中计算出来。例如,平均击球数是击中的垒数被击球数除的结果,也就是HITS/AT_BAT。多余数据使得维护和更新数据变得非常困难。对一个元素的简单的改变或是增加都会引起多个位置的改变和重新计算。
真正所需要的是一种不同的样式单语言,能使我们向元素中增加样板内容并根据现存的元素内容执行转换。这样的语言是存在的,这就是可扩展的样式语言(Extensible Style Language,简写为XSL)。
可扩展的样式语言(Extensible Style
Language,XSL)将在第14、15章中加以讨论。
CSS比XSL简单,对于基本的Web页面来说,也更适合一些,而且也是更为直接的文档。XSL变得相当复杂,但功能也更为强大。XSL是建立在我们已经在上面学到的简单的CSS格式化的基础之上的,但是也提供了将源文档转换为读者可以查看的不同形式的方法。在调试XML时,首先使用CSS寻找问题,然后再转到XSL,以便获得更大的灵活性,这通常是不错的主意。
在本章中,读者看到了几个展示如何从头创建XML文档的示例。我们特别学到了如下内容:
· 如何检查包括在XML文件中的数据,以便标识元素。
本章中充满了枯燥的代码。文档是在没有太多的细节的情况下编写出来的。在下一章中,我们将要探讨在XML文档中嵌入信息的附加意义,包括特性、注释和处理指令,并看一看在XML中用另一种对棒球统计数据编码的方法。
使用XML对一组给定的数据进行编码,有很多种方法。但是没有哪一种方法是唯一正确的,只是一些方法相较而言更可取,在特定的应用中更合适。本章采用前面章节中所用的棒球示例,仔细探讨使用XML创建棒球统计的不同方法。文中会特别强调使用属性存储信息和使用空标记定义元素位置。另外,鉴于CSS(级联样式单)对缺乏内容的XML元素执行起来并不顺利,我们将检验另一种功能更强大的样式单语言——XSL。
本章内容包括:
· 属性
在上一章中,所有的数据可分为标记名或者元素的内容两类。这种方法直接易懂,但不是唯一的。XML元素与HTML中的元素一样,有自己的属性。元素的每个属性是一个名称-数值对,名称和数值分别为一个字符串,一个元素不能有两个同名的属性。
大家都熟悉HTML的属性句法,请看下面的<IMG>标记实例:
<IMG SRC=cup.gif WIDTH=89 HEIGHT=67 ALT="Cup of coffee">
该标记有4个属性,SRC属性的值是cup.gif,WIDTH属性的值是89,HEIGHT属性的值是67,ALT属性的值是Cup of coffee。然而,与HTML不同,XML中属性的值必须加引号,并且必须有与起始标记匹配的终止标记。上述标记实例用XML表示为:
<IMG SRC="IMAGE\cup.gif" WIDTH="89" HEIGHT="67" ALT="Cup of coffee">
</IMG>
HTML与XML的另一个不同点是:XML没有赋予IMG标记及其属性任何特殊意义。特别是不能保证XML浏览器会把该标记翻译成装载并显示cup.gif文件中的图像的指令。
可以很容易将属性句法应用到棒球示例中,这样会使标记显得简洁明了。例如,我们可以用SEASON元素中的一个YEAR属性代替一个YEAR子元素:
<SEASON YEAR="1998">
</SEASON>
另一方面,LEAGUE应当是SEASON的一个子元素而不是一个属性。因为在一个赛季中可能有两个联赛,而且子元素在任何时候都有可能指代不同的事物。但是,一个元素的属性名是不能重复的。因此,不能像下面的示例那样编写SEASON元素。
<SEASON YEAR="1998" LEAGUE="National" League="American">
</SEASON>
LEAGUE确实是一个子元素而不是一个属性的另一个原因是,它含有子结构,可进一步分成多个DIVISION元素,其属性值是无格式文本。XML元素可对结构方便地加以编码,而属性值却不能。
联赛名称是无结构的普通文本,每一个联赛只有一个名称,因此,LEAGUE元素含有一个NAME属性,而不是一个LEAGUE_NAME子元素:
<LEAGUE NAME="National League">
</LEAGUE>
由于属性与元素的联系比子元素更加紧密,上述的属性名应使用NAME,而不是LEAGUE_NAME,不会出错。各分部和球队这些子元素同样有NAME属性,不必担心与联赛名混淆。一个标记可以有多个属性,只要这些属性不同名即可。我们可以将各队所在的城市看作一个属性,如下所示:
<LEAGUE NAME="American League">
<DIVISION NAME="East">
<TEAM NAME="Orioles" CITY="Baltimore"></TEAM>
<TEAM NAME="Red Sox" CITY="Boston"></TEAM>
<TEAM NAME="Yankees" CITY="New York"></TEAM>
<TEAM NAME="Devil Rays" CITY="Tampa Bay"></TEAM>
<TEAM NAME="Blue Jays" CITY="Toronto"></TEAM>
</DIVISION>
</LEAGUE>
如果把每一项统计选作一个属性,一个队员将包括许多属性。下面的示例是用属性表示的Joe Girardi在1998年的统计数据。
<PLAYER GIVEN_NAME="Joe" SURNAME="Girardi"
GAMES="78" AT_BATS="254" RUNS="31" HITS="70"
DOUBLES="11" TRIPLES="4" HOME_RUNS="3"
RUNS_BATTED_IN="31" WALKS="14" STRUCK_OUT="38"
STOLEN_BASES="2" CAUGHT_STEALING="4"
SACRIFICE_FLY="1" SACRIFICE_HIT="8"
HIT_BY_PITCH="2">
</PLAYER>
清单5-1应用这种新的属性样式展示了一个完整的XML文档,文档是1998年重要棒球联赛的统计。展示的信息与上一章清单4-1中的一样(包括2个联赛、6个分部、30个球队和9名运动员),只是标记的方式不同。图5-1显示了装入到Internet Explorer 5.0中的没有任何样式单的文档。

图5-1 1998年主要棒球联赛统计,使用属性表示信息
清单5-1:使用属性存储棒球统计的完整的XML文档
<?xml version="1.0" standalone="yes"?>
<SEASON YEAR="1998">
<LEAGUE NAME="National League">
<DIVISION NAME="East">
<TEAM CITY="Atlanta" NAME="Braves">
<PLAYER GIVEN_NAME="Marty" SURNAME="Malloy"
POSITION="Second Base" GAMES="11" GAMES_STARTED="8"
AT_BATS="28" RUNS="3" HITS="5" DOUBLES="1"
TRIPLES="0" HOME_RUNS="1" RBI="1" STEALS="0"
CAUGHT_STEALING="0" SACRIFICE_HITS="0"
SACRIFICE_FLIES="0" ERRORS="0" WALKS="2"
STRUCK_OUT="2" HIT_BY_PITCH="0">
</PLAYER>
<PLAYER GIVEN_NAME="Ozzie" SURNAME="Guillen"
POSITION="Shortstop" GAMES="83" GAMES_STARTED="59"
AT_BATS="264" RUNS="35" HITS="73" DOUBLES="15"
TRIPLES="1" HOME_RUNS="1" RBI="22" STEALS="1"
CAUGHT_STEALING="4" SACRIFICE_HITS="4"
SACRIFICE_FLIES="2" ERRORS="6" WALKS="24"
STRUCK_OUT="25" HIT_BY_PITCH="1">
</PLAYER>
<PLAYER GIVEN_NAME="Danny" SURNAME="Bautista"
POSITION="Outfield" GAMES="82" GAMES_STARTED="27"
AT_BATS="144" RUNS="17" HITS="36" DOUBLES="11"
TRIPLES="0" HOME_RUNS="3" RBI="17" STEALS="1"
CAUGHT_STEALING="0" SACRIFICE_HITS="3"