使用 XML: 了解解析 XML 的各种方法

使用 XML: 了解解析 XML 的各种方法

了解各种 XML 解析 API 的优缺点,为项目选择最适当的 API

级别: 中级

Benoît Marchal (bmarchal@pineapplesoft.com), 顾问, Pineapplesoft

2007 年 3 月 09 日

即便对高级 XML 问题具有丰富经验的开发人员也不一定就完全了解 XML 最基本的一些问题。为了为您打下坚实的基础,本文讨论了最基本的 XML 服务:解析。本文介绍了各种解析方法,着重说明了各自的优缺点。

了解基础

从 XML 的出现至今大约有 9 年的时间了。对于可扩展标记语言来说这是一段不短的历程。现在很难找到完全不用 XML 的应用程序了。

但是和客户在一起的时候,仍然不可避免地发现基础性的东西尚未被透彻地全部理解。对复杂的 XML 主题理解透彻的开发人员,最近却发现对基础性的东西(比如解析)的把握还存在很多不足,这真有点出人意料。

而对 XML 的处理是从何处开始的呢?没错,是解析。解析可能是开发人员能够使用的最基本的服务。解析器读取 XML 文档,解释语法并向应用程序传递有意义的对象。解析器可能还提供其他服务,比如验证(保证文档符合 XML Schema 或 DTD)或者名称空间解析。

本文介绍了各种解析方法,着重分析了各自的优缺点,帮助您在下一个项目中选择适当的工具。文中包含大量文章的链接,选择工具的时候可以详细研究给定的 API。

解析的重要性

解析为什么重要?因为所有 XML 处理都从解析开始。无论使用高层编程语言(如 XSLT)还是低层 Java 编程,第一步都是要读入 XML 文件,解码结构和检索信息等等,这就是解析。

解析文档时面临的第一个选择是采用现成的解析库(基本上每种编程语言都有,包括 COBOL [Common Business Oriented Language])还是自己创建一个。答案非常简单:选择现成的库。

坦白地说,XML 不是一种多么复杂的语法,因此认为可以自己通过正则表达式或其他特殊方法来解析的想法是可以理解的。但实际上却很难成功:XML 语法要求支持多种编码和很多难以捉摸的特性,比如 CDATA 节和实体。自定义的实现几乎很难照顾到所有这些方面,因而造成了不兼容性。

相反,随开发环境提供的解析器大都经过了与兼容性有关的测试。采用 XML 这样的标准语法的主要原因是兼容其他应用程序和工具箱,这是真正值得使用经过良好测试的库的情况之一。

多数解析器提供了至少两种 API,通常是一个对象模型 API 和一个事件 API(也称为 API)。比如,Java 平台同时提供了 DOM(文档对象模型)和 SAX(Simple API for XML)。

这两套 API 提供了相同的服务:文档解码、可选的验证、名称空间解析等等。差别不在于服务而在于 API 使用的数据模型。

关键的选择:第一种方法

对象模型 API 定义了层次化对象模型来表示 XML 文档。换句话说,对应 XML 语法中的每个概念定义相应的类:元素、属性、实体、文档。解析器读入 XML 文档的时候,建立 XML 语法和类之间的一对一映射。比如,每遇到一个标记,就实例化一个元素类。

毫不奇怪,对哪种数据模型最好存在一些争议。W3C 规范化了 DOM,它的主要优点是可移植性:它是作为一种 CORBA 接口定义的,被映射到很多语言。因此如果了解了 JavaScript 中的 DOM,也就知道了 Java、C++、Perl、Python 和其他语言中的 DOM。

另一种数据模型是 JDOM,一种针对 Java 优化的 DOM(专用于 Java),和 Java 语言结合得更紧密,但是按照定义缺乏可移植性。

尽管人们可以继续商讨对 XML 语法来说哪种数据模型最好,但我认为没有多少意义,因为各种基于对象的 API 其优点和不足基本上是一样的。从好的方面来说,如果熟悉 XML 语法的话,对象模型 API 更容易理解。因为它直接从 XML 语法映射到类,很容易学习、使用和调试。

简单的代价是效率,至少对很多项目而言是这样。读入文档的时候,解析器根据语法结构创建对象。对很多应用程序来说,XML 语法并不是很合适:

  • XML 语法非常罗嗦,即使文档很小,解析器也要创建很多对象。
  • 对 XML 词汇表进行的优化通常针对的是存储和数据传输效率,而不是处理,因而应用程序可能需要对数据进行预处理,比方说,在开始真正的处理之前,先计算部分和或者合并其他来源的数据。很多情况下,在处理之前必须将数据从 XML 对象模型复制到应用程序专用的对象模型或者数据库。
  • 因为这种对象模型是通用的,包含很多应用程序并不需要的对象之间的引用(比如,从子元素到父元素的反向引用)。这些引用进一步增加了内存消耗。

在桌面上处理小型文档这可能不是大问题,但是在其他环境中,比如服务器上,对象模型固有的低效率是不可接受的。

第二种方法

第二种选择是事件 API,比如 SAX。这个概念是上述对象模型方式的一种反映。只不过这种方法不根据 XML 语法定义通用的数据模型,其解析器依赖应用程序程序员建立定制的数据模型。

因此解析器可以做得更小,因为只需要传递最少量的信息。更重要的是,和一个型号打天下的对象模型(不管对象模型多么好)相比总的效率更高,程序员可以根据应用程序的需要定制对象模型。

它的优点很明显:

  • 统计应用程序或总结信息的任何应用程序都可以从中获益,因为它们的数据模型只需计算总计而无需复制整个文档。
  • 类似的,即使动态处理文档的应用程序(比如把文档加载到数据库中)不需处理或者只需少量处理,也可从中受益,因为根本不需要存储数据。

由于减少了内存需求,事件 API 可以处理任意大小的文档,包括大小超过可用内存的文档。基于同样的原因,这类 API 也非常适合多个进程并发执行和共享内存的服务器。

效率的代价是简单性的损失。事件 API 一向以难用著称,因为应用程序员要负责更多的操作。虽然短期看来如此,但根据我的经验,从中期和长期来看,效率上的改进足以抵消略微增加的复杂度。

流式 API 有两种形式:推式和拉式。从历史上看,推式方法更加流行,因为这正是 SAX 采用的模型。推式方法正在实现标准化,很快将作为 StAX 集成到 Java 平台中。

两者有什么区别呢?区别在于由谁控制读循环。和读取文件的任何软件一样,解析器也是围绕着读循环(读入文件的循环)创建的。

模式(SAX)下,解析器控制循环。实际上应用程序调用解析器的时候,在文件结束之前控制权不会返回给应用程序。前面已经提到,解析器回调应用程序以建立数据模型,解析器处于控制地位。

模式下,应用程序控制循环。循环中应用程序负责反复调用解析器,直到文件结束。

推模式最适合边读入边处理 XML 文档,比如读入 RSS 提要并显示为 HTML 网页。对于使用 XML 存储数据的多数应用程序来说,“读文档”用对解析器的一次调用实现最方便。

拉模式更适合于处理不同 XML 词汇表的文档。这类应用程序通常需要嗅探输入(读入前几行)以根据词汇表决定调用子例程。

对于控制解析器的应用程序而言,一次循环是必要的,因为应用程序很容易在嗅探前面几行之后停止读入。

第三种方法

如果不提到另一种选择,即 XML 编组库形式的解析,如 Castor,本文就不完整。该方法介于对象模型和事件方法之间。

其思想是从 XML Schema 生成一个对象模型而不是通用模型(如 DOM),解析器生成更加针对所用词汇表的数据模型。比方说,如果词汇表处理的是发货单,那么可以预料其中会包含发送方、接收方、日期、产品类别、产品标识、单价和总价。DOM 将这些元素映射到一个一般性的元素类。编组库 为发送方、接收方、日期、产品类别、产品标识、单价、总价和文档中出现的其他元素创建专门的类。

从处理的是根据词汇表定制(与根据应用程序的需要定制可能相同,也可能不同)的而不是通用数据模型这方面来讲,编组库具备事件 API 的一些优点。

如何写入 XML 呢?

解析器读取和解码 XML 文档,将其从磁盘上转到内存中。那么另一个方向上的移动该如何处理呢?如果应用程序需要将数据存储到 XML 文件中怎么办?

虽然我建议您避免使用特殊的例程解码 XML 文档,但是对于写入 XML 没有这样的疑虑。读的时候必须保证实现了所有的规则,包括一些隐晦之处。但是写入的时候,则可以实现一个小型的、可工作的词汇表子集。

但是多数对象模型 API 仍然承担了双重职责,除了读以外还要能将对象树写入磁盘。如果使用事件 API,就可以从数据结构生成写事件(请参阅参考资料)。

结束语

那么结论是什么呢?用于读 XML 文档的 API 对应用程序的总体性能有重要影响,因此一定要花时间熟悉各种选项,为您的平台、编程语言,更重要的是为您的项目做出最佳选择。

一般而言,事件 API 占用的资源更少,因此效率更高,但是如果无论如何都要将整个文档保存到内存中,那么对象 API 更好一些,因为可以节省大量代码。

请参阅 参考资料 列表,其中包括很多有关使用 XML 解析器的文章。最重要的是,不要用特殊的代码来解码 XML 文档。如果没有完全实现标准,就会造成兼容性问题,这样的风险太高了。



参考资料

学习
  • 您可以参阅本文在 developerWorks 全球网站上的 英文原文

  • 理解 DOM”(Nicholas Chase,developerWorks,2003 年 7 月):了解 DOM,一种标准的文档对象模型 API。文中介绍了 DOM 文档的结构以及如何使用 Java 技术从 XML 文件创建文档、修改和检索结果。

  • SAX,功能强大的 API”(Benoît Marchal,developerWorks,2001 年 8 月):了解何时使用 SAX 代替 DOM,文中还介绍了常用的 SAX 接口,包括很多详细的 Java 应用程序的例子。

  • 技巧: 用于灵活处理的SAX 过滤器”((Uche Ogbuji,developerWorks,2003 年 7 月):使用 SAX 筛选器创建可维护性更好的代码,从简单、独立的模块开始创建复杂的 XML 处理行为。

  • 用 JDOM 简化 XML 编程”(Wes Biggs 和 Harry Evans,developerWorks,2001 年 5 月):讨论了针对 Java 语言优化的另外一种对象模型 API。

  • Castor JDO 入门”(Bruce Snyder,developerWorks,2002 年 10 月):深入了解 SAX API。

  • 超越 DOM、SAX 和 XSLT 的限制”(David Mertz,developerWorks,2001 年 10 月):考察了每种模型的局限性和可能的改进方法。

  • 技巧: 实现 XMLReader”(Benoît Marchal,developerWorks,2003 年 3 月):了解如何通过 SAX API 写入 XML 文档。

  • 技巧:从 DOM 转换” 和 “技巧:从 SAX 转换”(Brett McLaughlin,developerWorks,2001 年 4 月):在 DOM 和 SAX 之间来回转换以便与应用程序进行通信。

  • IBM XML 认证:了解如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员。

  • XML 技术库:developerWorks 中国网站 XML 专区提供了大量技术文章和技巧、教程、标准以及 IBM 红皮书。

  • developerWorks 技术活动网络广播:随时关注技术的最新进展。


获得产品和技术
  • 使用 IBM 试用软件 构建您的下一个开发项目,这些软件可直接从 developerWorks 下载。


讨论


关于作者

Benoit Marchal 的照片

Benoît Marchal 是一位比利时籍顾问。他是 XML by Example, Second Edition 和其他几部 XML 书籍的作者。可以通过 bmarchal@pineapplesoft.com 或他的个人网站 www.marchal.com 与他联系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值