XML中的实体声明:超越基础的进阶理解
一说起XML的实体,很多人脑子里先蹦出来的可能就是那几个基础的预定义实体,比如 < 代表小于号。这没错,但XML的实体系统远比你想象的更强大、更灵活。今天,我们就来深入聊聊那些在复杂DTD和大型文档中扮演关键角色的实体——不仅仅是文本替换那么简单。
未析实体:当XML遇到非XML数据
我们都知道,并非所有数据都是XML格式的。比如一张JPEG图片,或者一段MPEG视频。XML该如何在文档中“引用”这些内容呢?答案就是“未析实体”。它更像是一个指向外部非XML资源的“快捷方式”。
在DTD中,你可以这样定义一个未析实体:
注意看最后的 NDATA a vi。这里的 a vi 并非随意书写,它必须是一个在 NOTATION 中预先声明过的MIME媒体类型标识符。NDATA的作用就是明确告诉处理器:“嘿,这个实体指向的数据不是XML,它的类型是a vi。”
不过,有一说一,直接在XML文档中嵌入这类未析实体在实践上比较复杂,也不算很规范,所以除非有特殊需求,一般建议尽量避免使用。
参数实体:DTD内部的“宏”与模块化利器
如果说通用实体是给文档内容用的“变量”,那么参数实体就是专门服务于DTD本身的“宏定义”。它在大型、模块化的DTD设计中不可或缺。
它的定义语法和通用实体很像,只是中间多了一个百分号 %:
使用时,也需要带上百分号:%person;。处理器会直接用字符串 name,address,postcode 来替换它。这有什么用呢?最直接的,它能避免在DTD中反复输入一长串相同的元素列表,提高可维护性。
但参数实体更强大的地方在于实现DTD的模块化。想象一下,像DocBook这样的庞大DTD,长度可能超过一万行,全部塞在一个文件里简直是维护者的噩梦。聪明的做法是把它按功能拆分成多个文件。
这时,参数实体就派上用场了。你可以在主DTD文件中这样写:
%names;
第一行定义了一个参数实体 %names;,它引用了外部的 names.dtd 文件。第二行通过引用这个参数实体,实际上就把整个外部DTD子集的内容“包含”了进来。这种结构让管理和维护大型DTD变得清晰、可行。
条件化处理:用INCLUDE与IGNORE实现灵活开关
你有没有想过,DTD里的某些声明能不能像代码一样,根据需要“开启”或“关闭”?XML确实提供了这种机制,那就是 INCLUDE 和 IGNORE 节。
IGNORE 块内的声明会被处理器忽略,效果类似注释:
]]>
当然,你也可以用 达到相同目的。INCLUDE 则恰恰相反,它明确表示其内的声明有效:
]]>
单独看,这个 INCLUDE 似乎有点多余,有没有它声明都有效。但妙处在于,当它们和参数实体结合时,就能产生奇妙的化学反应。
我们可以先定义一个参数实体作为“开关”:
然后,在声明处使用这个参数实体,而非直接使用关键字:
]]>
这样一来,元素 note 的声明是否生效,就完全由参数实体 %note_allowed; 的值决定了。只要在DTD别处将 %note_allowed; 重定义为 IGNORE,对应的声明就会瞬间“失效”。这种模式为DTD提供了强大的条件化包含和功能定制能力。
说到底,XML的这些高级实体特性,其核心思想是相同的:抽象与复用。无论是引用一段外部文本、一个非XML资源,还是组织复杂的DTD结构,目的都是让文档和它的模式定义更加清晰、灵活且易于管理。掌握它们,才算真正读懂了XML设计哲学中关于“可扩展”的那一部分。
本文由本站(https://www.jb51.net)整理发布!转载请注明出处,谢谢!
