E N D
高能物理 焦健斌 2003.9 XML语言介绍
(I)简介 • XML,或称为可扩展标记语言(Extensible Markup Language),是一种您可以用来创建自己的标记的标记语言。它由万维网协会(W3C)创建,用来克服 HTML(即超文本标记语言(Hypertext Markup Language),它是所有网页的基础)的局限。和 HTML 一样,XML 基于 SGML —标准通用标记语言(Standard Generalized Markup Language)。尽管 SGML 已在出版业使用了数十年,但其理解方面的复杂性使许多本打算使用它的人望而却步(SGML 也代表“听起来很棒,但或许以后会用(Sounds great, maybe later)”)。XML 是为 Web 设计的。
我们为什么需要 XML • HTML 始终是最成功的标记语言。您几乎可以在任何设备(从掌上电脑到大型机)上查看最简单的 HTML 标记,并且您甚至可以用合适的工具将 HTML 标记转换成语音和其它格式。既然 HTML 成功了,为什么 W3C 还要创建 XML 呢?为了回答这个问题,请查看下面这个文档: • <p><b>Mrs. Mary McGoon</b> <br> 1401 Main Street <br> Anytown, NC 34829</p> HTML 的问题在于它是为人设计的。即使不用浏览器查看上面的 HTML 文档,您和我也会知道那是某个人的邮政地址。(具体而言,它是美国某个人的邮政地址;即使您一点也不熟悉美国邮政地址的格式,您可能也会猜出这表示什么。) • 作为人,您和我具有理解大多数文档的含义和意图的智慧。遗憾的是机器不能做到。尽管这个文档中的标记告诉浏览器如何显示该信息,但标记没有告诉浏览器信息是什么。您和我知道它是一个地址,但机器不知道。
显示 HTML • 要显示 HTML,浏览器只需遵循 HTML 文档中的指令即可。段标记告诉浏览器在新的一行显示,并且通常在前面有一个空行,而两个换行标记则告诉浏览器前进到下一行,并且行之间没有空行。尽管浏览器出色地将文档格式化,但机器仍不知道这是地址。
样本 XML 文档 • 现在让我们来看一个样本 XML 文档。使用 XML,您可以给文档中的标记赋予某种含意。更重要的是,机器也容易处理这样的信息。您只需通过找到 <postal-code>和 </postal-code>标记之间的内容(技术上称为 <postal-code>元素),就可以从该文档抽取邮政编码。
简单例子 • <address> • <name> • <title>Mrs.</title> • <first-name> Mary </first-name> • <last-name> McGoon </last-name> • </name> • <street> 1401 Main Street </street> • <city state="NC">Anytown</city> • <postal-code> 34829 </postal-code> • </address>
标记、元素和属性 • 标记是左尖括号(<)和右尖括号(>)之间的文本。有开始标记(例如 <name>)和结束标记(例如 </name>) • 元素是开始标记、结束标记以及位于二者之间的所有内容。在上面的样本中,<name>元素包含三个子元素:<title>、<first-name>和 <last-name>。 • 属性是一个元素的开始标记中的名称-值对。在该示例中,state是 <city>元素的属性。
XML 正如何改变着 Web • 既然您已经看到开发人员可以使用 XML 创建具有自我描述性数据的文档,让我们看看人们正如何利用这些文档来改进 Web。以下是几个关键领域: • XML 简化了数据交换。因为不同组织(乃至同一组织的不同部门)很少就单一工具集形成标准,所以要使应用程序相互交流需要进行大量工作。使用 XML,每个组织可以创建单一的实用程序,该实用程序将该组织的内部数据格式转换成 XML,反之亦然。最好有这样的机会:这些组织的软件供应商已经提供了在它们的数据库记录(或 LDAP 目录,或采购订单等等)与 XML 之间进行相互转换的工具。 • XML 支持智能代码。因为可以使 XML 文档结构化以标识每个非常重要的信息片段(以及这些片段之间的关系),所以可以编写无需人工干预就能处理这些 XML 文档的代码。软件供应商花费了大量时间和金钱来构建 XML 开发工具这一事实意味着编写那样的代码是相对简单的过程。 • XML 支持智能搜索。尽管搜索引擎这些年在稳步改进,但从搜索中得到错误的结果仍很常见。如果您正在搜索包含名叫“Chip”的人的 HTML 页面,您可能还会找到有关功克力片、计算机芯片、木片以及许多其它无用匹配的页面。搜索 XML 文档查找包含文本 Chip的 <first-name>元素会给您一个好得多的结果集。
(II)XML文档规则 • 如果您研究过 HTML 文档,您会熟悉使用标记来标注文档文本的基本概念。本章讨论 HTML 文档和 XML 文档之间的区别。它介绍了 XML 文档的基本规则,并讨论了用于描述它们的术语。 • 关于 XML 文档,有一点很重要:XML 规范需要解析器拒绝任何没有遵守基本规则的 XML 文档。大多数 HTML 解析器接受随意的标记,它们会猜测文档作者的意图。为了避免一般的 HTML 文档中松散结构所造成的混乱,XML 的创造者们决定从一开始就强制文档结构。 • (如果您不熟悉解析器,那么顺便说明一下:解析器是尝试读取文档并解释其内容的一段代码。)
无效、有效以及格式良好的文档 • 有三种 XML 文档: • 无效文档没有遵守 XML 规范定义的语法规则。如果开发人员已经在 DTD 或模式中定义了文档能够包含什么,而某个文档没有遵守那些规则,那么这个文档也是无效的。 • 有效文档既遵守 XML 语法规则也遵守在其 DTD 或模式中定义的规则。 • 格式良好的文档遵守 XML 语法,但没有 DTD 或模式。
需要注意的几点 • 元素是区分大小写的 • 元素不能重叠 • 必须有根元素 • 必须有结束标记 • 属性取值要加引号
根元素 • XML 文档必须包含在一个单一元素中。这个单一元素称为根元素,它包含文档中所有文本和所有其它元素。在下面的示例中,XML 文档包含在一个单一元素 <greeting>中。请注意文档有一行注释在根元素之外;那是完全合乎规则的。 • <?xml version="1.0"?> • <!-- A well-formed document --> • <greeting> • Hello, World! • </greeting> • 而不包含单一根元素的文档不管该文档可能包含什么信息,XML 解析器都会拒绝它。
结束标记是必需的 • 不能省去任何结束标记。 • 如果一个元素根本不包含标记,则称为空元素;HTML 换行(<br>)和图像(<img>)元素就是两个例子。在 XML 文档的空元素中,您可以把结束斜杠放在开始标记中。下面的两个换行元素和两个图像元素对于 XML 解析器来说是一回事: • <img src="../img/c.gif"></img> • <img src="../img/c.gif" />
属性必须有用引号括起的值 XML 文档中的属性有两个规则: • 属性必须有值 • 那些值必须用引号括起。 您可以使用单引号,也可以使用双引号,但要始终保持一致。
XML 声明 • 大多数 XML 文档以 XML 声明作为开始,它向解析器提供了关于文档的基本信息。 • <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
XML 文档中的其它项 • 注释<!--……--> • 处理指令<?......?> • 实体:<!ENTITY dw "developerWorks"> • 无论 XML 处理器在何处找到字符串 &dw;,它都会用字符串 developerWorks代替该实体。
名称空间 • <customer_summary xmlns:addr="http://www.xyz.com/addresses/" xmlns:books="http://www.zyx.com/books/" xmlns:mortgage="http://www.yyz.com/title/" > • ... <addr:name><title>Mrs.</title> ... </addr:name> ... • ... <books:title>Lord of the Rings</books:title> ... • ... <mortgage:title>NC2948-388-1983</mortgage:title> ... • 名称空间定义中的字符串仅仅是字符串。对,这些字符串看似 URL,其实不是。您可以定义 xmlns:addr="mike",那也是有效的。名称空间唯一的重要性在于其唯一性;这就是为什么大多数名称空间定义看起来象 URL 的原因。XML 解析器不会到 http://www.zyx.com/books/去搜索 DTD 或模式,它只是把那个文本作为字符串使用。这有些令人困惑,但名称空间就是这样工作的。
(III)定义文档内容 定义用来表示数据的元素有两种方法。 • 一种方法是使用文档类型定义(Document Type Definition),或简称 DTD。DTD 定义可以在 XML 文档中出现的元素、这些元素出现的次序、它们可以如何相互嵌套以及 XML 文档结构的其它详细信息。DTD 是最初的 XML 规范的一部分,与 SGML DTD 非常相似。 • 另一种方法是使用 XML Schema。模式可以定义您能在 DTD 中使用的所有文档结构,它还可以定义数据类型和比 DTD 更复杂的规则。W3C 在提出最初的 XML 规范的几年之后开发了 XML Schema 规范。
文档类型定义 • DTD 允许您指定 XML 文档的基本结构。 • <!-- address.dtd --> • <!ELEMENT address (name, street, city, state, postal-code)> • <!ELEMENT name (title? first-name, last-name)> • <!ELEMENT title (#PCDATA)> • <!ELEMENT first-name (#PCDATA)> • <!ELEMENT last-name (#PCDATA)> • <!ELEMENT street (#PCDATA)> • <!ELEMENT city (#PCDATA)> • <!ELEMENT state (#PCDATA)> • <!ELEMENT postal-code (#PCDATA)>
该 DTD 定义了样本文档中使用的所有元素。它定义了三个元素: • <address>元素包含一个 <name>、一个 <street>、一个 <city>、一个 <state>和一个 <postal-code>。所有这些元素必须出现,而且必须以这个顺序出现。 • <name>元素包含一个可选的 <title>元素(问号表示 title 这个元素是可选的),后面跟有一个 <first-name>和一个 <last-name>元素。 • 所有其它包含文本的元素。(#PCDATA代表已解析字符数据;不能在这些元素中包含另一个元素。) • 尽管 DTD 相当简单,但它清楚地说明了什么样的元素组合是合乎规则的。<postal-code>元素在 <state>元素之前的地址文档是不合乎规则的,没有 <last-name>元素的文档也不合乎规则。 • 另外,请注意,DTD 语法不同于普通的 XML 语法。(相反,XML Schema 文档本身就是 XML,这导致一些有趣的结果)。尽管 DTD 的语法不同,但您仍可以将普通的注释放到 DTD 中。
DTD 中的符号 • <!ELEMENT name (title?, first-name, (middle-initial | middle-name)?, last-name)> • 逗号表示项的列表。 • 问号表示这一项是可选的;它可以出现一次或根本不出现。 • 加号表示这一项必须至少出现一次,但可出现任意次。 • 星号表示这一项可以出现任意次,包括零次。 • 竖线符号表示选择列表;您只能从列表选择一项。
灵活性说明 • 如要定义一个 XML 文档的结构,您应该象在应用程序中设计数据库模式或数据结构那样事先考虑 DTD 或模式。您事先考虑的未来需求越多,以后实现它们就越容易而且成本越低。
定义属性 可以定义将在 XML 文档中出现的元素的属性。使用 DTD,您还可以: • 定义哪些属性是必需的 • 定义属性的缺省值 • 列出给定属性的所有有效值 <!ELEMENT city (#PCDATA)> <!ATTLIST city state CDATA (AZ|CA|NV|OR|UT|WA) "CA" postal-code CDATA #REQUIRED>
XML 模式 • 使用 XML 模式,您会有更多的能力来定义什么样的 XML 文档是有效的。它们与 DTD 相比有几个优势: • XML 模式使用 XML 语法。换句话说,XML 模式是一个 XML 文档。这意味着您可以象处理任何其它文档一样处理模式。例如,您可以编写一个 XSLT 样式表,该样式表将 XML 模式转换成具有自动生成的 JavaScript 代码的 Web 表单,其中的 JavaScript 代码可以验证您输入的数据。 • XML 模式支持数据类型。尽管 DTD 确实支持数据类型,但很明显这些数据类型是从发布的角度开发的。XML 模式支持 DTD 中的所有原始数据类型(诸如标识和标识引用之类的类型)。它们还支持整数、浮点数、日期、时间、字符串、URL 和其它对数据处理和验证有用的数据类型。 • XML 模式是可扩展的。除了 XML 模式规范中定义的数据类型以外,您还可以创建自己的数据类型,并且可以基于其它数据类型派生出新的数据类型。 • XML 模式有更强的表达能力。例如,您可以用 XML 模式定义任何 <state>属性值不得超过 2 个字符,或定义任何 <postal-code>元素的值必须与正则表达式 [0-9]{5}(-[0-9]{4})?相匹配。您无法用 DTD 做这些事。
定义模式中的元素 • <xsd:element>元素定义了一些 XML 元素。头两个定义的元素(<address>和 <name>)由其它元素组成。<xsd:sequence>元素定义了包含在这两个元素中的元素的序列。 • 与 DTD 版本一样,XML 模式示例定义 <address>包含一个 <name>、一个 <street>、一个 <city>、一个 <state>和一个 <postal-code>元素,并且依照上面的次序。请注意,该模式实际上用 <xsd:complexType>元素定义了新的数据类型。 • 大多数元素包含文本;定义它们很简单。您只要声明新的元素,然后指定它的数据类型为 xsd:string: • <xsd:element name="title" type="xsd:string"/> <xsd:element name="first-Name" type="xsd:string"/> <xsd:element name="last-Name" type="xsd:string"/> <xsd:element name="street" type="xsd:string"/> <xsd:element name="city" type="xsd:string"/>
定义模式中的元素内容 • 样本模式为两个元素的内容定义了约束:<state>元素的内容必须是两个字符长,而 <postal-code>元素的内容必须与正则表达式 [0-9]{5}(-[0-9]{4})?匹配。 • 对于 <state>和 <postal-code>元素,模式用约束定义了新的数据类型。第一个情况使用 <xsd:length>元素,而第二个情况则使用 <xsd:pattern>元素定义该元素必须匹配的正则表达式。
(IX)API(应用程序接口) • XML 有多种编程接口。这些接口为开发人员使用 XML 文档提供了一致的接口。有许多 API 可以使用: • 文档对象模型(Document Object Model (DOM)) • 用于 XML 的简单 API(Simple API for XML (SAX)) • 用于 XML 解析的 Java API(Java API for XML Parsing (JAXP) • ……
文档对象模型DOM • 文档对象模型(通常称为 DOM)为 XML 文档的已解析版本定义了一组接口。解析器读入整个文档,然后构建一个驻留内存的树结构,然后您的代码就可以使用 DOM 接口来操作这个树结构。您可以遍历树以了解原始文档包含了什么,您可以删除树的几个部分,还可以重新排列树和添加新的分支,等等。
DOM 和基于树的处理 • 使用 DOM 时,数据以类树结构被装入内存中。 • 同一文档将被表示为节点,如左边所示。 • 矩形框表示元素节点,椭圆形表示文本节点。 • DOM 使用根节点和父子关系。例如,在本例中,samples 将是带有五个子节点的根节点:三个文本节点(空白)和两个元素节点 server 和 monitor。 • 实际上,server 和 monitor 有 null 值。相反,它们有子文本节点 UNIX 和 color。
DOM 问题 • DOM 构建整个文档驻留内存的树。如果文档很大,就会要求有极大的内存。 • DOM 创建表示原始文档中每个东西的对象,包括元素、文本、属性和空格。如果您只需关注原始文档的一小部分,那么创建那些永远不被使用的对象是极其浪费的。 • DOM 解析器必须在您的代码取得控制权之前读取整个文档。对于非常大的文档,这会引起显著的延迟。 • 这些仅仅是由文档对象模型的设计引起的问题;撇开这些问题,DOM API 是解析 XML 文档非常有用的方法。
用于 XML 的简单 API • SAX 解析器向您的代码发送事件。当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,它会告诉您。您可以决定什么事件对您重要,而且可以决定要创建什么类型的数据结构以保存来自这些事件的数据。如果您没有显式地保存来自某个事件的数据,它就被丢弃。 • SAX 解析器根本不创建任何对象,它只是将事件传递给您的应用程序。如果希望基于那些事件创建对象,这将由您来完成。 • SAX 解析器在解析开始的时候就开始发送事件。当解析器发现文档开始、元素开始和文本等时,代码会收到一个事件。您的应用程序可以立即开始生成结果;您不必一直等到整个文档被解析完毕。更妙的是,如果您只查找文档中某些内容,代码一旦找到所要找的东西就可以抛出一个异常。该异常会停止 SAX 解析器,然后代码用它找到的数据做它需要做的任何事。
SAX 处理是如何工作的 • SAX 分析经过其的 XML 流,考虑以下 XML 代码片断: <?xml version="1.0"?> <samples> <server>UNIX</server> <monitor>color</monitor> </samples> • SAX 处理涉及以下几步: • 创建事件处理程序。 • 创建 SAX 解析器。 • 将事件处理程序分配给解析器。 • 对文档进行解析,将每个事件发送给处理程序。
SAX 问题 • 这种处理的好处非常象流介质的好处;可以立即开始分析,而不是必须等待所有要处理的数据。同样,由于应用程序简单地检查经过其的数据,所以不需要将数据存储在内存里。当遇到大文档时,这是一个突出的优势。一般来讲,SAX 还比其替代物“文档对象模型”快。 • 另一方面,由于应用程序不以任何方式存储数据,所以,使用 SAX 时,不可能对数据进行更改,或者“返回”至数据流中前面的数据。
在 SAX 与 DOM 之间如何选择 选择 DOM 还是 SAX,这取决于几个因素: • 应用程序的目的:如果必须对数据进行更改,并且作为 XML 将它输出,则在大多数情况下,使用 DOM。与使用 XSL 转换来完成的简单结构更改不一样,如果是对数据本身进行更改,则尤其应该使用 DOM。 • 数据的数量:对于大文件,SAX 是更好的选择。 • 将如何使用数据:如果实际上只使用一小部分数据,则使用 SAX 将数据抽取到应用程序中,这种方法更好些。另一方面,如果知道将需要向后引用已经处理过的信息,则 SAX 可能不是正确的选择。 • 需要速度:通常,SAX 实现比 DOM 实现快。 • SAX 和 DOM 不是互斥的,这一点很重要。可以使用 DOM 来创建事件的 SAX 流,可以使用 SAX 来创建 DOM 树。事实上,大多数解析器实际常常使用 SAX 来创建 DOM 树!
选择合适的API • 要用 Java 编写应用程序吗?JAXP 使用 DOM、SAX 和 JDOM;如果您用 Java 编写代码,那么您应使用 JAXP 将您的代码与各种解析器实现的细节隔离。 • 应用程序将如何部署?如果您的应用程序将要作为 Java applet 部署,那么您会希望使要下载的代码数量最小,别忘了 SAX 解析器比 DOM 解析器小。还要知道使用 JDOM 时,除了 SAX 或 DOM 解析器之外还要求编写少量的代码。 • 一旦解析了 XML 文档,还需要多次访问那些数据吗?如果您需要回过头来访问 XML 文件的已解析版本,DOM 可能是正确的选择。而 SAX 事件被触发时,如果您以后需要它,则由您(开发人员)自己决定以某种方式保存它。如果您需要访问不曾保存的事件,则必须再次解析该文件。而 DOM 自动保存所有的数据。 • 只需要 XML 源文件的少量内容吗?如果您只需要 XML 源文件的少量内容,那么 SAX 可能是正确的选择。SAX 不会为源文件中的每个东西创建对象;您要确定什么是重要的。使用 SAX,您要检查每个事件以了解它是否与您的需要有关,然后相应地处理它。更妙的是,一旦找到您正在寻找的东西,您的代码就会抛出一个异常来完全停止 SAX 解析器。 • 您正在一台内存很少的机器上工作吗?若是的话,不管您可能考虑到的其它因素是什么,SAX 是您的最佳选择。