http://www.ibm.com/developerworks/cn/xml/x-tipcomp.html
如果有很好的压缩方法,何必需要二进制 XML?
二进制 XML 引起了很多讨论,一个诱因是需要相对紧凑的传输格式,尤其是在 Web 服务中的应用。已有的现成解决方案是压缩。这篇技巧说明了如何使用压缩准备在 Web 服务中传输的 XML 文件。
关于 XML 的讨论中, 二进制 XML的观点一直不绝于耳。由于其文本传统,再加上为了对国际化文本的友好性而要求的一些规则,XML 非常冗长。等价的二进制形式要紧凑得多。很久以前(2000 年)的一篇文章“XML The future of EDI?”(请参阅 参考资料)中,我曾经示范了把 ANSI EDI X12 订单事务(二进制形式)的一部分转化为 XML。得到的 XML 比原来的 EDI 消息长八倍多(其他一些 XML/EDI 试验项目的结果似乎只有三倍左右)。这种冗长性给 XML 的存储带来了一些问题,但至少在今天存储器非常便宜。传输能力通常受到更大的限制,对二进制 XML 最响亮的呼声就来自那些使用 XML 作为消息传输格式的用户,包括一些 Web 服务用户。
压缩 XML 的一种方法是采用一种针对二进制重新设计的格式。领先的候选对象是 ISO/ITU ASN.1,一种先于 XML 出现的数据传输标准。更新后的 ASN.1 提供了一些和 XML 有关的能力,可以把 XML 格式重新表示成专门的形式,如 ASN.1 Packed Encoding Rules,它定义了一种非常紧凑的二进制编码。OASIS UBL 是其中的一个例子,该计划采用 ASN.1 方法压缩 XML 数据。
压缩 SOAP 编码
如果需要在 Web 服务中传输 XML,您可能会发现有效负载太长了。这种情况下您可以对 XML 内容使用多种文本压缩选项中的一种。清单 1 是前述文章中所提供的那个 XML/EDI 例子。
清单 1. Web 服务交换的示例 XML 文档
<?xml version="1.0" encoding="UTF-8"?> <PurchaseOrder Version="4010"> <PurchaseOrderHeader> <TransactionSetHeader X12.ID="850"> <TransactionSetIDCode code="850"/> <TransactionSetControlNumber>12345</TransactionSetControlNumber> </TransactionSetHeader> <BeginningSegment> <PurposeTypeCode Code="00 Original"/> <OrderTypeCode Code="SA Stand-alone Order"/> <PurchaseOrderNumber>RET8999</PurchaseOrderNumber> <PurchaseOrderDate>19981201</PurchaseOrderDate> </BeginningSegment> <AdminCommunicationsContact> <ContactFunctionCode Code="OC Order Contact"/> <ContactName>Obi Anozie</ContactName> </AdminCommunicationsContact> </PurchaseOrderHeader> <PurchaseOrderDetail> <Name1InformationLOOP> <Name> <EntityIdentifierCode Code="BY Buying Party"/> <EntityName>Internet Retailer Inc.</EntityName> <IdentificationCodeQualifier Code="91 Assigned by Seller"/> <IdentificationCode>RET8999</IdentificationCode> </Name> <Name> <EntityIdentifierCode Code="ST Ship To"/> <EntityName>Internet Retailer Inc.</EntityName> </Name> <AddressInformation>123 Via Way</AddressInformation> <GeographicLocation> <CityName>Milwaukee</CityName> <StateProvinceCode>WI</StateProvinceCode> <PostalCode>53202</PostalCode> </GeographicLocation> </Name1InformationLOOP> <BaselineItemData> <QuantityOrdered>100</QuantityOrdered> <Unit Code="EA Each"/> <UnitPrice>1.23</UnitPrice> <PriceBasis Code="WE Wholesale Price per Each"/> <ProductIDQualifier Code="MG Manufacturer Part Number"/> <ProductID Description="Fuzzy Dice">CO633</ProductID> </BaselineItemData> </PurchaseOrderDetail> </PurchaseOrder>
原来的 EDI 例子只有 200 个字节长,而这个 XML 版本有 1721 字节长。
知名的 PK-ZIP 例程能够把这个 XML 文件压缩到 832 个字节。
GNU gzip 例程则把该文件压缩为 707 个字节。
bzip2 中的开发源代码例程把该文件压缩到 748 个字节。
所有这些压缩格式都不如专门的 EDI 格式紧凑,但 EDI 格式不容易理解。bzip2 由于和 gzip 相比对很多文件有更好的压缩效率(在较慢的压缩速度下)而闻名,但是据我的观察上述结果并非个例,就是说对于 XML 的处理 gzip 要好于 bzip2。
目前的多数平台和语言都提供压缩库,至少包含 PK-ZIP 和 GNU gzip 压缩算法,可以在调用 Web 服务之前通过编程进行压缩。
一定要分析标准化(C14N)是否有助于具体实例的压缩。C14N 是生成 XML 文档物理表示——称为标准形式——的标准化方法,以便解决 XML 语法在不改变含义的情况下所允许的细微变化。根据粗略的经验方法,如果 XML 是手工编辑的,属性的顺序和空格的使用可能有各种变化,C14N 可能会改进大型文档的压缩性能。但是如果 XML 是机器生成的或者使用了大量空白元素,C14N 可能是有害的。上述例子更接近后一种情况。我使用 PyXML 项目中的 C14N 模块进行了标准化处理。Python 代码如下所示:
>>> from xml.dom import minidom >>> from xml.dom.ext import c14n >>> doc = minidom.parse('listing1.xml') >>> c14n.Canonicalize(doc) >>> f = open('listing1-canonical.xml', 'w') >>> c14n.Canonicalize(doc, output=f) >>> f.close()
得到的文件 listing1-canonical.xml 有 1867 个字节,使用 gzip 压缩后还有 714 个字节。未压缩的文本多出了 146 个字节,gzip 压缩后的结果多出了 7 个字节。主要的原因是空白元素在 C14N 之后用最冗长的形式表示。比如,下面这一行:
<Unit Code="EA Each"/>
就变成了
<Unit Code="EA Each"></Unit>
要把 gzip 之类例程压缩后的 XML 绑定到 SOAP 中,有两种办法可供选择:
- 使用某种形式的附带工具。
- 对消息主体内容使用 Base64 这样的编码。
Base64 只使用普通的文本字符呈现二进制文档。使用任何平台上准备好的库就可以完成这项工作。Base64 编码数据甚至还有一个 W3C XML Schema,如果正确设置 Web 服务,您的工具就能够自动实现 Base64 的编码和解码操作。不幸的是,Base64 编码部分抵消了压缩效果。Base64 编码要比原来的文档大一些,比例大概是 4:3。使用 Base64 编码后,清单 1 的 gzip 压缩结果是 957 个字节。
结束语
一般说来,如果对 XML 文件应用 gzip,并且压缩后的结果采用 Base64 编码,在 SOAP 中联机传输,结果文件通常只有原来的一半大小。这可能足以满足您在 XML Web 服务中节省空间的需要。如果不能满足,就好好地看一看 ASN.1。