XML中嵌入二进制数据的三种方式

http://www.ibm.com/developerworks/cn/xml/x-binary/

XML 已经改变了完全不同的应用程序 ― 这些应用程序使用不同的技术、操作平台和位置 ― 之间交换、共享和传输数据的方式。对于所有这些数据的运动,为达到可伸缩性,必须记住的唯一一点是通过启用 HTTP 的标记来包装数据。通过 HTTP 发送数据的最佳途径是使用 XML,有许多原因可以说明它比 HTML 好。

最初,HTML 被认为只能处理文本,但是现在它还普遍用于引用和标记非文本数据。所以 XML 步 HTML 后尘是十分自然的。因为 XML 不必遵循特定的语法(象 HTML 那样)并且比 HTML 更具可扩展性,所以人们可以以他们想要标记各类数据的任意方式来使用它。尽管如此,HTTP 通常仍作为传输层使用;因此 XML 在处理二进制数据时必定受到许多约束。当二进制数据是用户或客户机应用程序请求的全部信息的一部分时,XML 只用于标记该二进制数据。而且,在 XML 中包含二进制数据的优点是便于在 HTTP 上传送数据。

演示应用程序:GODOWN 和 AGENT

在应用程序之间有时候会传输非文本数据。例如,设想有两个 B2B 应用程序:GODOWN 和 AGENT。GODOWN 维护公共库中的所有存货;AGENT 是销售界面。当用户在 AGENT 上选中一个产品名时,就向 GODOWN 提出有关该产品详细信息的请求。以下是操作技巧部分。例如,如果该产品是一块手表,那么应用程序可能显示诸如价格、制造商地址和手表图片等详细信息。如果该产品是一盒迈克杰克逊 CD,那么详细信息可能包括用户可以播放的样品媒体文件。本文将关注于通过 XML 进行数据(二进制数据)定义和描述,以及如何访问这个二进制数据。

正如您会发现的那样,在开发时不能决定数据的本质,并且可能需要经 HTTP 传送该数据来提供通用访问。XML 是描述和定义数据的一种非常便利的框架,所以我们使用它来定义库存中的产品 ― 库存由 GODOWN 应用程序来维护。

让我们看一下这两个应用程序之间进行数据传输过程中的不同阶段。以下是一个数据请求进程的生命周期:

  1. 从 AGENT 向 GODOWN 发出带 ProductId 的 HTTP 请求。
  2. GODOWN 获取标识并为所需的产品编制 XML 文档。
  3. 然后用 AGENT 应用程序理解的方式格式化这个 XML 文档,并以字节流的形式将它发送回 AGENT。有两种方法可以完成这个操作:
    • 从 GODOWN 将 XML 文档传送到 AGENT,这样生成的文件格式为 agentproduct1.xml(请参阅下面显示的代码)。
    • 解密 XML 数据并将二进制数据编码为字符串格式,然后将之转储到 CDATA 节中并发送到 AGENT。这使 AGENT 的作业稍微简单些。生成的文件格式将是 agentproduct2.xml(请参阅下面显示的代码)。
  4. AGENT 接收来自 GODOWN 的字节流并对其进行相应处理。

三种方法

现在,我们需要将一个应用程序的二进制数据传送到另一个应用程序中。让我们看一下用 XML 文件表示二进制数据或将二进制数据嵌入到 XML 文件中的三种方法,这里简要地列出了这三种方法,下面还有更详细信息:

  • 第一种:通过外部实体和标记法的方式来表示二进制数据
  • 第二种:使用 MIME 数据类型来表示二进制数据
  • 第三种:将二进制数据嵌入 CDATA 节中

虽然这三种方法都可以用来表示二进制数据,但不是每种方法都可以适用于任意给定的情况中,而且一种方法未必能很好地替代另一种方法。换句话说,可以同时应用这些方法使之相互协作相互补充。为了详细阐述这一点,让我们考虑示例应用程序。如果您必须维护库中所有产品的详细信息,(以便能对任何产品的请求提供服务),那么最佳方法是使用方法 1 以单一大型 XML 文件的形式来表示数据。甚至方法 2 也适用于这一目的,但是在数据的可扩展性上有某些限制(实体重用等)。当必须传送数据时,如果数据大小相对比较小,那么我们应该应用方法 3。另一种选择是首先高速缓存实体名称和 systemid,然后将详细信息发送到另一个应用程序或方法来处理(下载/显示)数据。在确定哪种方法最适合给定的情况时,尽管没有指导原则或标准,您还是可以识别哪种方法最适合于您的情况。现在应该更详尽地查看这些方法了。

第一种方法 ― 通过外部实体和标记法的方式来表示二进制数据 ― 用来描述数据。正如我们已经看到的,在使用 Request 和 Response 范例的数据访问中本质上有三个阶段。它们是:

  • 描述数据并将之存储在一个公共区域以供访问。
  • 将请求的数据提供给客户机(响应)。
  • 在客户机应用程序中表示已提供的数据(经过一些修改/变换)。

这里,以 标记法(称为 外部实体)的形式描述二进制数据,通过使用 DTD 来保持数据的持久性。我们应使用第一种方法来进行二进制数据的描述和存储。甚至第二种方法也适用于相同的目的 ― 这两种方法的区别在于是否使用 DTD 的应用程序。如果不能确定是否需要 DTD,那么有一个简化的和范围缩小的选项:当产生请求这样一个二进制数据时,可以通过使用 HTTP 将它发送到客户机;然后,客户机应用程序完成一些转换并向用户表示。如果客户机是启用 HTTP 的,象一些浏览器,那么可以将这个 XML 数据转换为 HTML 并显示。但是如果最终用户环境不启用 HTTP,那么我们必须分别下载所有引用的二进制数据源。在这样的情况中,我们必须使用方法 3。当出现请求二进制数据时,读取二进制数据源并打包到 XML 文档中作为以 base64 编码的字符。将之发送到客户机。这个阶段的缺点是数据传送需要更多等待时间,读取和编码需要更多处理时间。当客户机应用程序读取这个 XML 文件时,它对 base64 字符进行解码,将二进制数据写入本地文件系统的一个文件中,并使用那个引用来向最终用户表示这一数据。方法 3 非常适合于二进制数据源不是一张图片或能显示的东西的特定情况;它可以是一个数字签名或一个可执行文件。

通过外部实体和标记法的方式来表示二进制数据

这是在 XML 文档中表示二进制数据最好的方法。虽然许多人会争论 DTD 使它变得混乱,但是我仍将建议这是非常有效的方法,因为数据在 XML 文档中模块化,并且从 DTD 中抽取二进制数据(源、文件名等)的详细信息。要以这种方法完成操作,您需要了解 XML 和 DTD。查看下面的样本 XML 和 清单 2中的 DTD。


清单 1. 样本 XML 产品文件:product.xml
<?xml version="1.0"?>
<!DOCTYPE godown SYSTEM "product.dtd">
<godown>
<product id="1">
  <name>watch</name>
  <category>HMT Shakthi</category>
  <description>SG CS 07 A</description>
  <price>Rs.500</price>
  <deliverytime>2 days</deliverytime>
  <display object="watch1"></display>
</product>
</godown>

XML 文件应遵循 DTD 并且通常包含许多产品标记,每个标记包含一个产品和它的详细信息。当 AGENT 应用程序向 GODOWN 应用程序发送一个或多个标识时,GODOWN 应用程序对 XML 文件进行语法分析并获取与产品相关的所有详细信息,然后将它们发送回服务器。在向服务器发送详细信息前,它应按照 AGENT 要求的格式重新编制数据。 清单 3中显示了一个这样的输出格式。转换成这个格式的 Java 程序是 agent1.java,在 binaryxml.zip(请参阅 参考资料)中提供了该程序。

这个格式不涉及处理二进制数据;它只描述和提供二进制数据。AGENT 应用程序通过使用 URL 能直接引用二进制数据,或者它能从 URL 下载文件来保留本地副本。如果它必须在浏览器中显示信息,那么它能直接引用信息,因为它有链接。在这种方法中,我们获得格式良好的 XML。查看文件 agent1.java(请参阅 参考资料)来了解它如何对 XML 文件和 DTD 进行语法分析,以便搜集有关外部实体引用的信息。

使用 MIME 数据类型来表示二进制数据

这种方法使用 MIME 数据类型来描述二进制数据/数据源时有两种方法:

  • 使用可扩展邮件传送协议(XMTP)或者类似的 MIME 编码服务器/应用程序
  • 将不同的 MIME 类型映射成 XML 文件中的二进制数据类型

让我们看一下这两种方法的优缺点。

在第一种方法中 ― 使用 XMTP,将软件的可使用性限制于启用 XMTP 的应用程序,或者在不使用启用 XMTP 的软件时,必须用另一个 XML 解码器来打包这个层。这意味着我们还必须实现第二种方法,而且我们还要有可用于同一目的的一些 XMTP API ― 因此这没有使我们有更好的适应性。另外,我们所关注的是用 XML 而不是用 XMTP 来表示二进制数据,所以我们将集中讨论第二种方法 ― 映射不同的 MIME 类型及其资源。对 MIME 和 XMTP 更感兴趣的人,请参阅 参考资料,从 openhealth.org 获得实现信息。

映射 MIME 类型和二进制数据

当我们必须映射 MIME 类型和二进制数据时,我们又有两个选择。第一个是用 DTD 中的二进制资源来系统地表示 MIME 类型。由于在本文的前面部分我们已经看到了类似表示,所以让我们来看一些不同的表示。DTD 只用于定义文档格式,并且将在 XML 文件本身中定义 MIME 类型。虽然总可以使用 DTD 来验证,但是这种方法的最佳用途是减少对 DTD 的依赖并使之成为自我描述的 XML 格式。由于它是自我描述的,所以它是可进化的方法。通过使用 XML 文件中的 MIME 类型,让我们来查看用于定义 product.xml 的变体版本的方法。


清单 4. product.xml 版本 2:product-mime.xml
<?xml version="1.0"?>
<godown>
   <product id="1">
       <category>HMT Shakthi</category>
       <description>SG CS 07 A</description>
       <price>Rs.500</price>
       <deliverytime>2 days</deliverytime>
       <display type="picture" mime="image/jpeg">
           <file>
                sgcs07a3.jpg
           </file>
       </display>
  </product>
</godown>

除了显示标记不同外,这个 XML 文件遵循与原始 product.xml 文件相同的结构。它描述了二进制数据文件的元数据。所以,当 AGENT 应用程序发送标识时,mimeagent.java(在 binaryxml.zip 中)对标识的 product-mime.xml 进行语法分析并将输出的 XML 文件生成为 agentproduct2.xml(请参阅下面的代码)。agentproduct1.xml 和 agentproduct2.xml 之间的唯一区别是后者在 CDATA 节中包含文件内容,而不是文件名称。我们正在将二进制内容嵌入 XML 文件本身而不是在 XML 文件中描述它的来源。下面显示了 agentproduct2.xml 的一个样本格式。


清单 5. 输出格式 2:agentproduct2.xml
<?xml version="1.0"?>
<selectedproducts>
   <product id="1">
       <name>watch</name>
       <category>HMT Shakthi</category>
       <description>SG CS 07 A</description>
       <price>Rs.500</price>
       <deliverytime>2 days</deliverytime>
       <display type="picture" mime="image/jpeg" name="sgcs07a3.jpg">
          <![CDATA[
               Binary data like..
                  /9j/4AAQSkZJRgABAAEAyADIAAD//g
               that are not parsed go here
           ]]>
       </display>
   </product>
</selectedproducts>

在用方法 1 和方法 2 解释 XML 数据的客户机应用程序之间有一个显著的区别。在方法 1 中,客户机应用程序只须转换可用的 XML 数据并以 HTML 或一些其它格式显示。通过使用 XSL、CSS 等可以很好地实现。然而,在方法 2 中,客户机应用程序必须解释 CDATA 节并将打包的二进制数据解码为原始格式,然后必须将它存储到本地文件系统中。存储在本地的二进制数据源用于进一步引用。表示之前需要一个处理阶段。使用这个方法之前必须注意这一点。

我们已经揭示了将二进制数据流发送回 AGENT 应用程序的第二个方法。第三种将使 AGENT 应用程序能够处理 XML 数据并生成用户可以查看/访问的数据的目标版本。这就完成了我们 B2B 应用程序的第 3 阶段。

将二进制数据嵌入 CDATA 节中

上面的方法用 XML 处理数据的表示,但是这种方法完全用 XML 来 包装数据。它主要应用在当将数据以 XML 格式发送回客户机应用程序时。当客户机接收到这种格式的数据时,或者如果客户机高速缓存这种格式的 XML 数据/文件,客户机应用程序非常容易向用户提供 XML 数据。在最后一种方法中,我们看到输出的 XML 文件包含了 CDATA 节来包装二进制数据。当客户机应用程序接收到这种数据时,它能以多种方法处理数据,例如:

  • 将整个数据保存为一个文件。
  • 将数据转换为 HTML、WML 等然后显示它。

让我们尝试将数据转换为 HTML 文件。这涉及两个阶段。 第一阶段,我们必须将二进制数据保存到各自的文件中,然后创建一个带引用那些二进制文件的新 HTML 文件。如果二进制文件是一张图像,那么我们将在 HTML 中使用 img 标记来引用二进制文件;否则,我们必须使用适当的标记,例如用于 shockwave 文件的 embed 。与此相关的信息存储到显示标记的 MIME 属性中。

将 XML 转换为 HTML 遵循将 XML 标记映射到 HTML 代码段这样一个简单映射。每个 XML 元素映射为某个产品(如手表或音乐 CD)的 HTML 表中的一行。所以,每个产品在 XML 中都可以表示为一个有五六个元素的节点,而同一产品在 HTML 中表示为一个含有四行的表。标记表示为头,内容还是表示为内容。

还有其它的 XML-到-HTML 的转换技术,象 CSS 或 XSL。在这个特殊实例中,我们正在处理 CDATA 节,其中存储了二进制数据并且需要将该数据持久保存为客户机系统中的一个文件。最简单的选项是使用从 XML 到 HTML 的简单的、定制映射,但是我鼓励那些感兴趣的人尝试基于 XSL 的转换。

您可以参考 binaryxml.zip 中的 HTML 文件,其中您将看到那个 HTML 文件的图片。这只是几种可能的方法中的一种;它能被转换为 WML 或一些其它格式,这样客户机能访问尽可能多的变体版本的 XML 信息。


图 1. 样本输出
输出 HTML 格式  

我们已经完成了 B2B 应用程序中的第 4 阶段,在这一阶段中 AGENT 应用程序将 XML 转换为 HTML 文件。本文研究了将二进制数据嵌入到 XML 中的三种方法并探究了将 XML 转换为其它方式的持久性数据的方法。

执行样本程序

执行样本 Java 程序以产生如上所示的各个输出。要使程序完美运行,我们需要在类路径(classpath)中放置 crimson.jar 和 soap.jar。在 JAXP XML 包中有 crimson.jar。您可以从 Apache(请参阅参考资料)上下载 soap.jar。

结束语

我希望您觉得本文值得一读。这只是我们将 XML 使用到 B2B 应用程序之间的数据传输的许多方法中的一种;还有许多其它方法,例如 XML-RPC 和 XML-EDI,它们可以替代这些方法。但是这是帮助您理解如何使二进制数据和 XML 一起协调工作的非常好的开端。


参考资料


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二进制XML储方案BinXML实现方法 BinXML是我个人杜撰出来的名字,不知道是否合适,也不知道是否已在类似的解决方案。在vcer.net上我将前一段时间的这个BinXML方案贡献出来,希望能够与广大vcer分享。 当时问题的背景是这样的,项目需要确定储方案,这种方案需要满足如下要求: · 生成的单机板exe尽可能的不依赖于其它软件,如:数据库管理系统; · 储的数据最好能够方便的拷贝,以满足项目的上报、汇总的功能; · 储的格式以后可能还会变,变化不频繁,但是最好能支持这种变化; · 只需要数据的持久化与反持久化的功能,不需要查询统计等复杂功能; · 储的数据可能包括一个或者多个的文件附件,如:word文档; · 储的数据可能被不同的平台使用,如:windows或者linux; · 作为web项目的一部分,除了提供VC的接口之外,储的数据格式需要提供java的接口; 我首先考虑到了MsAccess格式,文件型数据库,方便拷贝,而且表的设计很柔性化。在windows环境下,MsAccess似乎可以不需要再安装额外的驱动,但是在linux环境下,如何被java调用是个问题(当然,这个问题也是可以解决的,但是很别扭)。其次,使用RDB还有一个问题:数据的层次表达与多值问题,将树型数据扁平化储的方案是有的,但是,将几层简单的节点拆分成N个表格,岂不是杀鸡用牛刀? 其次的考虑当然是XML,然而XML是基于可读文本的,如何解决二进制数据问题?当然可以通过编码的途径来解决,但是这样使用XML是不是太牵强了?而且,XML有个缺陷,数据都是文本型的。要使用数值型、布尔型、日期型的数据,需要做进一步的解析。 那么就使用自定义的数据文件格式?传统情况下,我们会用一个或者若干个struct将数据打包,一下子塞进文件。但是现在用户说了:我们现在定义的数据结构可能会变:) 看来,我们的储方案还必须要足够的柔性化。 想到最后,我决定还是借鉴XML的树型标签形式,来实现一种二进制储结构,即BinXML:) 关于src包 对于BinXML-src里面的例子,数据的结构类似于: BinXML-src.zip包括两部分,一部分是vc的工程,一部分是java的工程,在Visual C++6.0和eclipse+JDK1.4都已编译、运行通过。大家可以运行BinXML-bin.zip里面的exe程序,测试一下BinXML文件的加载和保功能。 对象模型 BinXML的对象模型主要包括:文档(document)和节点(node)。每个文档包含一个根节点(root),根节点下面包含一个或者多个子节点,如此类推。 文档包含一个文件头,其包含了一些标识串、操作系统版本、文件大小、文档创建时间等信息。 每个节点也包含一个节点头,表明该节点的名称以及大小。一般来说,用户不会直接接触到文件头和节点头这些信息。 如下为VC里的类声明: 在java里,定义了binxml.io包,其的类、接口定义与以上相似。 你很容易想到,使用BinXML,可以很方便地完成CTreeCtrl的持久化。 如何使用? 如下是VC加载BinXML的代码片断: 对应的,写入BinXML的代码片断: 是不是很简单?在java里面,BinXML的一个测试用例: TODO 希望BinXML能给你带来一定的帮助和启发,更多的是希望能带来启发:)希望有兴趣的哥们继续完善BinXML,别忘了在你的大名之前保留我的版权信息: BinXML需要完善的地方表现在: · 暂时只实现了string/long/byte[],还没有提供其它类型的解析与转换; · XML标签不支持属性,只支持子元素:) · 是不是可以提供一个oxm模型,完成BinXML与Object之间的直接映射,现在这个映射工作还是人工代码实现的,譬如:GetString/GetLong,等等。应该可以完善一个映射机制,自动根据定义好的类型进行转换;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值