XML 的使用越来越广泛,但是很多 XML 的结构并不好。即便结构良好,也常常设计得很糟,使得处理和维护非常困难。而大部分用于 XML 的基础结构使问题更加恶化。于是出现了关于 XML 最佳实践的公开讨论,比如 Henri Sivonen 的文章“HOWTO Avoid Being Called a Bozo When Producing XML”。Uche Ogbuji 经常在 IBM developerWorks 上讨论 XML 最佳实践,这里他提出了在这些文章中讨论的要点。
几年来我一直在本专栏和其他系列文章中讨论 XML 最佳实践。其他人,比如和我同行的专栏作家 Elliotte Rusty Harold 也谈到这个问题。参加 XML 设计原则讨论的 XML 专家越好越好,这样社区就会对在不同层次上采用 XML 的开发人员提供一致的建议。本文将结合最新和过去的文章,更详细地介绍了 XML 最佳实践。
不再有笨蛋
Henri Sivonen 撰写了一篇有用的文章“HOWTO Avoid Being Called a Bozo When Producing XML”(请参阅参考资料)。他采用了基于 XML 的 Web 提要格式(如 RSS 和 Atom)的观点,提出了用名称空间生成结构良好的 XML 应该做和不应该做哪些事情的指南。正如他在简介中所说的:
有些开发人员似乎认为以编程方式生成 XML 而保持结构良好非常困难(如果不是不可能的话),另一些开发人员却能够做到这一点,并奇怪其他人为什么如此无能。我假设没有人愿意显得无能或者被点名。因此,我希望下列建议能够帮助开发人员从第一类人转变成第二类人。
Henri 给出的第一条建议是“不要将 XML 看作是文本格式”,我认为这是一条危险的建议。当然其基本观点是正确的 —— 不能像简单的文本文档那样随心所欲的生成和编辑 XML,但是这种要求适用于所有有结构的文本格式。但是,说 XML 不是文本就背弃了 XML 一个最重要的特点,而这点在规范的 XML 定义中被奉为圭臬。(“文本对象是结构良好的 XML 文档[如果符合本规范]”。) Henri 的提法也让人糊涂,因为有关于 XML 文本的技术定义,大致上是将它解释为 XML 的字符序列。文本不仅仅是叶子元素或者属性中的主要成分 —— 技术上称这类文本为字符数据。文本还是所有 XML 实体的主要成分,因此说 XML 不是文本是自相矛盾的。我认为强调 XML 和开发人员已经熟悉的文本格式的区别会更有意义。
上面的评述表明,Henri 的建议可能由于过分关心生成结构良好的 Web 提要的问题而有些偏激。警告人们简单的堆砌字符串,期望它成为结构良好的 XML 的做法是危险的,这一点上他是正确的。我也在文章中建议人们使用成熟的 XML 工具箱而不是使用简单的文本工具来创建 XML(请参阅参考资料)。我疑虑的是 Henri 描述这个建议的方式有点混乱,在更广泛的 XML 处理上下文中会造成误解。他在“Don't use text-based templates”和“Don't print”两节中反复重申这个观点。我认为可以将他的建议归纳为“不要使用不能保证产生结构良好的 XML 的机制。”这确实是一项很重要的建议。正像 Herni 所提到的,安全创建 XML 的一种方法是发送 SAX 事件,“使用树或栈(或者 XML 解析器)”。但即使这样做也不能令您高枕无忧。使用的 SAX 工具不一定要进行所有必要的结构良好性检查。比如,XML 中禁止某些 Unicode 字符。为了解决这些问题可能需要进行额外的检查。
Henri 建议用户不要尝试手工管理名称空间,这是正确的。我曾经在 developerWorks 上讨论过,必须非常小心地处理 XML 名称空间。他建议开发人员,按照统一名[名称空间统一资源标识符(URI)加上本地名]来考虑一般情况就行了,但有时候不可避免地要面对前缀或者 XML 声明。在 XSLT 这样的规范中,QName(前缀/本地名组合)可在属性值中使用,并假定前缀根据作用范围内的名称空间声明解释。这种模式称为上下文中的 QName。在这种情况下,开发人员必须控制声明的前缀,否则 XML 处理就会失败。如果开发人员管理自己的名称空间声明,由于 XML 名称空间的复杂性,结果往往会显得杂乱无章。
因为经过 XML 处理管道之后名称空间语法可能变得非常混乱,一种解决方法是在管道的最后插入一个规范化步骤。XML 规范化消除了 XML 1.0 和 XML 名称空间允许的各种语法变体,包括不同的名称空间声明方式。规范化不能消除使名称空间声明对开发人员变得危险的所有问题。规范化也不能解决上下文中的 QName 问题,因为它并没有改变文档中使用的前缀,但它确实可以减轻名称空间声明的混乱程度,使您很容易确定问题所在,甚至可以编写代码自动纠正这些问题。GenX 库是 Henri 建议使用的 XML 创建工具之一,能够自动生成规范的 XML,其他很多工具箱也作为选项提供了规范化功能。
Henri 关于 Unicode 和字符处理的建议基本上是完全正确的。不过我认为“Avoid adding pretty-printing white space in character data”一节有点夸大其词。多数情况下,元素之间而不是带有字符数据的元素内部的精细打印是安全的。如 Henri 所述,清单 1 所示的如果以清单 2 的形式呈现通常是不安全的。
清单 1. XML 例子
<foo>bar</foo>
清单 2. 在字符数据中增加空白后的 XML 例子
<foo>
bar
</foo>
但通常以清单 3 的形式打印 XML 是安全的,输出结果如清单 4 所示。
清单 3. 另一个 XML 例子
<doc><foo>bar</foo></doc>
清单 4. 清单 3 中的 XML 在字符数据中增加了空格
<doc>
<foo>bar</foo>
</doc>
很多 XML 序列化工具能够理解相对安全和不安全的的打印格式。必须知道的是,如果在混合内容中增加空格,则清单 3 和 4 中所示的精细打印形式可能造成扭曲。如果使用模式制导的序列化,则可以避免这类问题。但在实践中,使用混合内容的多数词汇表对空白规范化没有这么敏感,因此不用过于担心精细打印。应该充分了解该问题,并知道没有办法关闭精细打印(最好默认不用精细打印)。Henri 提出了清单 5 所示的精细打印实践,但是我不同意,因为我认为那些难看的标记不容易理解。
清单 5. Henri Sivonen 建议但本文作者不同意的精细打印方式
<foo
>bar</foo
>
修道院的建议
现在换换档,本文要探讨的第二篇资料是 Simon St. Laurent 撰写的“Monastic XML”(请参阅参考资料)。这是一组小短文,围绕着如何充分利用 XML 而就处理和思考 XML 提出了一些建议。Simon 使用修道院和禁欲主义作为比喻,提出为 XML 增加不适应其简单文本根 (textual root) 的过多负担是危险的。 在“Marking-up at the foundation”中,他讨论了字符数据和标记(元素和属性)的本质作用。在“Naming things and reading names”中,他解释了为何一般标识符(也称为元素类型名)是一个重要的概念,应该作为标记信息结构的惟一关键成分。理想情况下,如果使用 XML 名称空间,关键就是统一名称(名称空间 URI 加上本地名),这种复杂化就是 Simon 在“Namespaces as opportunity”中厉声疾呼的原因之一。“Accepting the discipline of trees”揭示了 XML 一个不幸的秘密:尽管看起来 XML 的层次结构很容易扩展成图形结构,但实践证明用 XML 建模图有点困难。但目前为止,“Monastic XML”网站上最重要的建议是“优化标记的处理总是不成熟”。XML 是一种声明性技术,对很多开发人员来说,关于它的强大和不足有很多不实之词。那些尽量把 XML 设计和处理细节拉近的开发人员,从长期来看,通常使得处理更加困难。XML 成功的关键是关注需要抽象表示的信息的特点,将它与需要处理这些信息的系统的技术设计分离开来。
结束语
讨论 XML 最佳实践时总是有一些不同的观点,特别是在初期阶段,但听到不同的声音是一件好事。关于这个话题的参考资料很少,我将继续在本专栏中讨论它。如果对最佳实践有什么资料或者建议或者希望分享您的观点,请参加 Thinking XML 论坛上的讨论。