二进制 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
转载于:https://www.cnblogs.com/BlogNetSpace/archive/2008/07/06/1236882.html