http://www.ibm.com/developerworks/cn/xml/x-tipcomp.html
如果有很好的压缩方法,何必需要二进制 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。