内容


使用 XML

安全编码实践,第 4 部分

二进制数据的特殊情况

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 使用 XML

敬请期待该系列的后续内容。

此内容是该系列的一部分:使用 XML

敬请期待该系列的后续内容。

这是考察 XML 部署常见陷阱的 系列文章 的最后一部分。XML 已经诞生大约七年了!这期间,我曾经参与了大量的咨询项目,在很多 XML 技术研讨会上讲课。我发现一些误解和问题反复地出现,然后又出现一些新的误解和问题。我希望通过本系列文章介绍这些常见的陷阱并提供可行的解决方案。第 1 部分 考察了语法,第 2 部分 介绍了设计问题,第 3 部分 则讨论了验证。第 4 部分作为本系列的最后一部分,考察二进制数据。

文本与二进制数据

XML 用于多种类型的信息:图书、报告、数据库摘要、日志、元数据、业务文档(发票、订单、总帐等)、电子政务文档(如社会安全表、海关单据、税票)等等。从本质上说,XML 仅限于文本信息。多媒体成分如徽标、图像、图表、podcasts、蓝图和电影都以二进制形式存储,不能直接包含在 XML 文档中。(不过,使用可缩放向量图形 SVG 存储为 XML 文件的向量图像是一个例外。)

需要处理包含文本和二进制数据的混合文档的 XML 开发人员面临着特殊的挑战。基本上有两种选择:

  • 从 XML 链接到二进制数据。
  • 将二进制数据嵌入到 XML 文档中。

这两种技术各有其优点和不足。

一些历史渊源

谈到二进制文档,您可能偶尔会遇到外部未解析实体,如二进制数据。虽然实体包含在 XML 标准中,但我记得只有少数 XML 词汇表用到了实体。我建议不要使用实体,还有更好的替代方案。

要理解实体,就必须知道 20 年前推出 XML 的前身 SGML 时文件系统的状态。文件系统之间有很大的差异。一些系统采用层次结构,另一些则使用扁平或半扁平空间。因此 SGML 定义了一种更抽象的文件系统视图,称为实体。在 SGML 的方言中,实体 是资源或者文档文件的抽象表示。底层的实体管理器将这个抽象视图联系到实际的文档。如果要把文档从一台计算机转移到另一台计算机,可以修改实体管理器而不必动文档。实体可以是 SGML 文档、DTD 或者像二进制数据这种所谓的未解析实体。

实体在 XML 中又延续了下来(看一看 SAX 中的 EntityResolver 接口),但使用很有限:

  • 实体必须在 DTD 中声明。这是一个问题,因为很少有文档使用 DTD。
  • 引入实体是为提供了不同文件系统的一致视图。现在 URI 提供了更好的更标准的方法。

设计上的考虑

如果文档包含不同来源(XML 和其他)的数据,则应该相应地设计解析代码。除了很简单的词汇表,应该将 XML 解析分为两部分:一部分是处理二进制数据的专用组件,另一部分是管理不同文件之间关系和从第一部分中调用适当组件的组织者(参见图 1)。 一些模式,如 Fowler 的插件模式、Gamma 的构造器和原型模式,会有所帮助(请参阅参考资料)。

图 1. 组织者管理文件关系和调用专门化的组件
模型
模型

链接

链接和嵌入都是同样可行的战略,但是具有不同的品质,适用于不同类型的项目。

如果了解 HTML,就会熟悉链接在锚、图像、applet、插件等等中的应用。使用链接,文档可以作为单独的资源存储,一个文档可以包含对其他文档的引用。独立性是链接的主要优点:

  • 资源可以按照自己的步伐演化。比如,可以使用更高分辨率的徽标而不需要修改主文档。
  • 每种资源可以用最合适的软件编辑,因为都存储在单独的文件中。
  • 这种机制是高度可扩展的,添加新的数据类型不需要修改原来的词汇表。
  • 重用内容很容易,具有很高的伸缩性,因为资源可以链接很多次,但是只需要下载一次。

已经证明链接有助于 Web 的开发。很难想像,一个成功的 Web 会没有链接!但是链接也有自己的不足。链接可能过时,有时候甚至断开,造成用户体验的满意度降低(该死的 404 错误)。而且对最终用户来说链接更复杂。复制文档不仅要复制主文件还要复制它链接的文件,可能造成处理错误。

技术上的考虑

从标记的观点来看,链接是微不足道的。只需要保留一个属性(或者元素,虽然链接一般使用属性)来保存链接文档的 URI:

<ulink xlink:href=
  "http://www.marchal.com/en/photos/humour/phbd0001.jpg">photo</ulink>

虽然可以随便命名链接属性,W3C 已经在 XLink 中制定了一组标准属性(请参阅 参考资料)。

嵌入

嵌入是将二进制文档复制到 XML 文档中。必须遵守 XML 语法。更具体地讲:

  • XML 保留了少量可能出现在二进制内容中的字符(最主要的是 <&)。
  • XML 以 Unicode 编码为基础,这意味着给定字符根据当前编码可能有不同的表示。

假设二进制数据有三个字节:0xea0x510xa9。如果将这些字节映射为 Unicode 表中的字符,其表示将依赖于下面的编码:

Latin-1: êQ? 
ASCII: êQ© 
UTF-8: ?aQ??

另一方面,如果按原样存储二进制数据,最终可能得到非法的 UTF-8 序列和其他编码。由于不知道文档使用的编码,因此这不是一种可行的解决方案。

惟一可行的解决方案是将数据编码为一组安全的字符。最常用的算法是 base64。Base64 将三个字节编码为四个字符(前面的例子就是 6lGp)。XML Schema 已经将 base64 作为一种标准的 XML 数据类型,多数语言都有 base64 的实现(请参阅 参考资料)。

嵌入二进制数据的优缺点和链接正好相反:

  • 嵌入减少了错误,因为最终用户只有一个文件。
  • 嵌入的效率较低,因为信息重复,需要对数据使用 base64 编码。
  • 二进制数据不能直接访问,因此很难使用最适合的编辑器。
  • 应用程序必须解码所有内容,因此更难以扩展。

Microsoft Word 2003 是一种流行的应用程序,它在 XML 文档中用内嵌 base64 编码数据保存图像和多媒体信息。

XML 文档特殊情况

可能最容易出错的是在另一个文档中嵌入 XML 数据。具体来说,将嵌入的文档看作文本内容是完全错误的,它根本就不是文本内容。

问题不在于转义 —— 很容易用 < 代替 < 或者使用 CDATA 节 —— 问题在于编码。除非嵌入文档和被嵌入文档使用相同的编码 —— 这一点很难保证,否则就会出错。关于这种错误请参阅清单 1:

清单 1. 错误的嵌入
<?xml version="1.0" encoding="UTF-8"?>
<document>
   <title>Encoding problem</title>
   <data><![CDATA[<?xml version="1.0" encoding="ISO-8859-1"?>
      <text>
         <p xml:lang="fr">Les caractères accentués
            (Latin-1) seront mal compris!</p>
         <p xml:lang="en">Accentuated characters (Latin-1)
            will be misinterpreted!</p>
      </text>]]></data>
</document>

双重编码使得原来的文档无法阅读。惟一安全的方法是被嵌入文档使用 base64 编码,或者使用名称空间将两个词汇表结合到一个文档中,如清单 2 所示:

清单 2. 安全的方法
<?xml version="1.0" encoding="UTF-8"?>
<d:document xmlns:d="http://psol.com/2005/doc"
            xmlns:t="http://psol.com/2005/text">
   <d:title>Encoding problem</d:title>
   <d:data>
      <t:text>
         <t:p xml:lang="fr">Les caractères accentués
            (Latin-1) seront mal compris !</t:p>
         <t:p xml:lang="en">Accentuated characters (Latin-1)
            will be misinterpreted!</t:p>
      </t:text>
   </d:data>
</d:document>

打包

打包是链接和嵌入的折衷。想法是用链接增加灵活性(如前面所述的那样),但是新增加一个层次将多个文档结合成一个文件,如图 2 所示。

图 2. 打包多个文件
打包

常用的有两种格式:Multipurpose Internet Mail Extensions(MIME,最初用于多媒体邮件)和 Zip。我更喜欢 Zip,因为它提供了一种层次化的数据结构。

开放式文档(Open Document)就是 Zip 打包的一个例子,它是 OpenOffice.org 背后的标准词汇表。文本内容存储在 XML 文件中,XML 文件和文档中包含的图像压缩到一个 zip 包中。SOAP Message Transmission Optimization Mechanism (MTOM) 则是使用 MIME 打包多种类型内容的一个例子(请参阅 参考资料)。

Java 开发人员可通过 JavaMail API 使用 MIME,Zip 则内置在 java.util.zip 包的 Java SE 中。

结束语

XML 不是一个孤岛,很多词汇表需要包含二进制内容。首先要判断自己的需要。如果二进制内容很少使用,则将二进制数据内嵌在 XML 文档中可以让用户方便地找到。如果二进制内容是整个内容的重要的组成部分(比方说超过三分之一),那么可能使用链接或打包更加灵活。无论采用何种方法,一定要特别注意 XML 内容。应该将其看作二进制数据,更好的是通过 XML 名称空间将其合并到原来的文档中。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=100469
ArticleTitle=使用 XML: 安全编码实践,第 4 部分
publish-date=12012005