http://www.ibm.com/developerworks/cn/xml/x-entities/
在 XML 中添加实体
为文档创建文本宏
许多开发人员在 XHTML 中使用实体代替特殊字符,但是也可以在 XML 中定义实体来简化创作或者引用外部文档的内容。在我们创建文档类型定义(Document Type Definition,DTD)并试图减小它的表面复杂性以便适合人类阅读时,实体也能派上用场。本文将全面介绍 XML 实体,并展示如何在文档中利用它们的优势。
简介
XML 是一种受到良好支持的 Internet 标准,用于以一种特殊方式编码结构化数据:XML 编码的数据几乎能够用所有编程语言轻松解码,甚至可以使用标准文本编辑器由人类阅读或编写。许多应用程序,特别是遵循现代标准的 Web 浏览器,都可以直接处理 XML 数据。
XML 中的实体用于表示特殊字符(通常难以或不可能在标准键盘上输入),重用 XML 代码段,将文档组织为几个文件,以及简化 DTD 的编写。
什么是实体?
实体是对数据的引用;根据实体种类的不同,XML 解析器将使用实体的替代文本或者外部文档的内容来替代实体引用。本文中要介绍以下几种实体,了解它们的工作方式,以及如何在我们自己的 XML 文档中利用它们的优势。
- 字符实体
- 命名实体
- 外部实体
- 参数实体
所有实体(除参数实体外)都以一个与字符(&)开始,以一个分号(;)结束。XML 标准定义了所有 XML 解析器都必须实现的 5 种标准实体,尽管它们还支持其他实体。
- ' 是一个撇号:'
- & 是一个与字符:&
- " 是一个引号:"
- < 是一个小于号:<
- > 是一个大于号:>
我们经常会在 XHTML 和 XML 文档中看到和用到 &、< 和 > 实体,特别是那些通过展示示例来归档标记的文档。
字符实体
对于字符实体,我们可以用十进制格式(&#nnn;,其中 nnn 是字符的十进制值)或十六进制格式(&#xhhh;,其中hhh 是字符的十六进制值)来指定任意 Unicode 字符。
例如,大写字母 A 是 Unicode 字符 U+0065。如果想将其表示为一个字符实体,可以输入 A(十进制值)或 A(十六进制值)。另一个更有用的字符也许是 © —— 版权符号。这个版权符号的字符实体是 © 或 ©,因为它是 Unicode 字符 U+0169。
在进行其他实体相关的解析之前,将首先替代字符实体。对 XML 解析器而言,字符实体与直接输入指定字符的效果完全相同。字符实体类似于 C 编程语言中的三联符(trigraphs):只是特定字符的替代表示。
还应该注意到,尽管 XML 解析器可以毫不费力地处理 “任何” 字符实体,但这些字符实体可能对处理应用程序和显示系统毫无益处。确保能够访问适当的字体和编码支持系统,以便在文档中显示任何稀奇古怪的字符。当前字体中找不到某个适当的字符时,多数系统会显示一个某种占位符。
命名实体
命名实体(在 XML 规范中也称为内部实体)就是我们在谈论 “实体” 时所指的实体。命名实体在 DTD 或内部子集(即文档中 <!DOCTYPE> 语句的一部分)中声明,在文档中用作引用。在 XML 文档解析过程中,实体引用将由它的表示替代。
简单来说,实体就是宏,它们在我们处理文档时得到扩展。
XHTML 规范使用命名实体来表示 ISO 8859-1 (Latin 1) 字符集中包含的、但在多数键盘上没有的特殊字符。其他实体用于指定特殊字符和符号。这些标准实体受到 Web 浏览器的良好支持,甚至允许我们在只支持 7 位 ASCII 字符集的系统(是的,还有人在使用古老的大型机系统)上使用纯文本编辑器来编写 XHTML 文档。清单 1 提供了 XHTML 1.0 的特殊字符的简要列表。
清单 1. XHTML 1.0 的几个特殊字符
<!ENTITY ndash "–"> <!-- en dash,U+2013 ISOpub --> <!ENTITY mdash "—"> <!-- em dash,U+2014 ISOpub --> <!ENTITY lsquo "‘"> <!-- left single quotation mark,U+2018 ISOnum --> <!ENTITY rsquo "’"> <!-- right single quotation mark,U+2019 ISOnum --> <!ENTITY sbquo "‚"> <!-- single low-9 quotation mark,U+201A NEW --> <!ENTITY ldquo "amp;“"> <!-- left double quotation mark,U+201C ISOnum --> <!ENTITY rdquo "”"> <!-- right double quotation mark,U+201D ISOnum -->
如 清单 1(它是一块 XHTML 1.0 Special Characters 声明)所示,命名实体由字符实体替代。如 上一小节 所述,字符实体等同于在文档中直接输入的引用字符。
我们在文档中使用 – 时,它由 Unicode 字符 U+2013(短横线 - 字符)替代。由于 – 的替代文本是一个字符引用,因此它等同于输入一个短横线字符。
命名实体在整个文档被读取后进行解析。这样,一些命名实体完全可以引用其他命名实体,其原因是所有实体都将在它们被扩展之前声明。例如,清单 2 展示了两个实体,其中一个实体引用另一个实体。
清单 2. 引用实体的实体
<!ENTITY c "Chris"> <!ENTITY ch "&c; Herborth">
在一个文档中使用 &c; 将扩展为 Chris,&ch; 将扩展为完整的 Chris Herborth。
循环引用是实体中的错误。因创建了循环引用而使文档无效时,解析器会报告错误。
使用 Firefox、Safari 和 Chrome 时,这些实体引用的效果与在存储为 XML 文件的 XHTML 文档中的效果一样,但使用 Microsoft® Internet Explorer® 8 时效果不一样。我将在在文档中定义实体 小节中介绍如何声明命名实体。
外部实体
外部实体表示外部文件的内容。外部实体在有些情况下很有用,比如说,您在创建一本图书并且想将每一章存储为一个单独的文件。您可能会创建一组如 清单 3 所示的实体。
清单 3. 外部实体引用其他文件
<!ENTITY chap1 SYSTEM "chapter-1.xml"> <!ENTITY chap2 SYSTEM "chapter-2.xml"> <!ENTITY chap3 SYSTEM "chapter-3.xml">
现在,当您在主图书 XML 文件(参见 清单 4)中将这些实体放到一起时,这些文件的内容将插入在引用点。
清单 4. 将各章放到一起
<?xml version="1.0" encoding="utf-8"?> <!-- Pull in the chapter content: --> &chap1; &chap2; &chap3;
由于这些文件的内容被插入到 XML 文档中,因此它们也必须是有效的 XML,而且它们必须是平衡的。也就是说,在一个外部实体的引用文件中开始的任何元素也必须在那个文件中结束。当清单 4 中的 XML 文档被解析时,它将被读取为一个大文档,包含 chapter-1.xml、chapter-2.xml 和 chapter-3.xml 文件的内容;XML 处理应用程序并不介意文档写入到 4 个单独的文件中。
参数实体
参数实体只用于 DTD 和文档的内部子集中。它们使用百分号(%)而不是与字符,可以是命名实体或外部实体。
在 XHTML DTD 中,参数实体用于引用在外部文件中声明的 Latin 1,Special Characters and Symbols 实体集,以及用作快捷方式以重用 DTD 的某些部分,比如每个 XHTML 元素都支持的标准属性集(参见清单 5)。
清单 5. XHTML 1 DTD 中的一个参数实体
<!ENTITY % attrs "%coreattrs; %i18n; %events;"> <!ENTITY % coreattrs "id ID #IMPLIED class CDATA #IMPLIED style %StyleSheet; #IMPLIED title %Text; #IMPLIED" > <!ENTITY % i18n "lang %LanguageCode; #IMPLIED xml:lang %LanguageCode; #IMPLIED dir (ltr|rtl) #IMPLIED" >
如 清单 5 所示,参数实体可以引用其他参数实体。与命名实体一样,参数实体在整个文档被读取之后才被扩展。
在文档中定义实体
您也许已经注意到,实体使用 ENTITY 声明进行定义,既可以作为外部 DTD 的一部分(参见 清单 6),也可以作为文档的外部子集的一部分(参见 清单 7)。
清单 6. DTD 中的实体声明
<!-- 6.1 Named entity for site name: --> <!ENTITY dw "developerWorks"> <!-- 6.2 External entity for re-use: --> <!ENTITY bio SYSTEM "dw-author-bio.xml"> <!-- 6.3 Parameter entity for use in DTD --> <!ENTITY % English "en-US|en-CA|en-UK">
在声明命名实体时,指定实体的名称及其替代文本。替代文本可以包含字符实体、命名实体和元素等,但不包含参数实体。
清单 7. 内部子集中的实体声明
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [ <!ENTITY test-entity "This <em>is</em> an entity."> ]> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <Meta http-equiv="Content-Type" content="application/xhtml+xml;charset=utf-8"/> <title>Entities in XML</title> </head> <body> <h1>Entities in XML</h1> <p>&test-entity;</p> <p>You can use it anywhere you'd use a standard XHTML entity:</p> <pre>&test-entity;</pre> </body> </html>
清单 7 中的 XHTML 文档(在我的系统上保存为 entities.xml)在其内部子集中声明了一个名为 test-entity 的新实体。内部子集是 <!DOCTYPE> 声明的一部分,位于 DTD 的 PUBLIC 和/或 SYSTEM 标识符之后的方括号中。
浏览器目前似乎不支持内部子集中的外部实体或参数实体,这也许是一个安全措施。当这两种类型的实体与 Web 浏览器的呈现引擎一起使用时,可能被用于创建拒绝服务攻击或其他恶意文档。
在文档中使用实体
现在已经熟悉在文档中使用实体的机制;DTD 中的参数实体的工作方式与此相同。前面所示的清单展示了如何使用各种实体:
在需要反复输入相同的文本时,可以尝试利用实体。以下是一些不错的实体示例:公司的正式名称、正在进行文档记录的产品的名称、版权、商标、注册商标通知,以及电子邮件地址(参见清单 8)。
清单 8. 通过实体减少输入
<!ENTITY co "Father Karass' Olde Tyme Steambots,LLC"> <!ENTITY prod "Semi-Autonomous Security Servant (SASSbot)"> <!ENTITY c "Copyright © 2010 &co; All Rights Reserved."> <!ENTITY author "Chris Herborth (chrish@poBox.com)">
可能变化的事物(比如产品名称)非常适合使用实体,这与在程序源代码中声明常量很相似。当产品名称变化时,我们只需更新实体声明,不必搜索并替代所有文件(参见 清单 9)。
清单 9. 使用实体轻松更新不断变化的文档
<!-- Current name: --> <!ENTITY prod "Semi-Autonomous Security Servant (SASSbot)"> <!-- Old names preserved for posterity: --> <!-- Original R&D name: --> <!--ENTITY prod "Security Bot"--> <!-- Marketing name v1 --> <!--ENTITY prod "Security Servant Bot"--> <!-- Marketing name v2 --> <!--ENTITY prod "Autonomous Security Servant Bot"-->
结束语
基于 XML 的标准(比如 XHTML)定义了一个有用的实体库,这使得创建包含不能在标准键盘上直接输入的字符的文档成为可能。命名实体的作用类似于宏,允许您使用实体引用替代重复或难以输入的文本。尽管 Web 浏览器不支持外部实体,但是我们可以通过其他 XML 应用程序来使用它们创建复合文档,这使得标准化和重用文档的某些部分变得更容易。参数实体用于将外部声明拖到 DTD 中,或者用于创建 DTD 内部宏来改善可读性。
在 XML 文档的 DOCTYPE 声明中声明命名实体很简单,而且您可能已经知道如何在文档主体中使用它们。