- 目前的XML处理是这样:DOM和SAX都存在速度过慢、效率低下、使用不便的缺点。而下一代XML处理模型VTD-XML既可以简化XML编程,又可以简化XML处理模型的选择。
自问世以来的八年间,XML已经流行起来,成为一种开放、半结构化的数据格式,用于保存数据以及通过Web交换数据。由于具有简洁性和易读性,XML在应用开发人员当中备受欢迎,已成为企业架构中必不可少的一部分。
虽然很难列举XML的使用方式有几种,但有一点是可以肯定的::在进行其他操作之前,必须先对XML进行解析。实际上,选择合适的解析器往往是企业开发人员在项目中必须处理的早期决策之一。而这种决策一次又一次地归结为对两种流行的XML处理模型的选择:文档对象模型(DOM)和用于XML的简单API(SAX)。
乍一看,DOM和SAX各自的优缺点似乎是互为补充的:DOM在内存中建立对象图;SAX基于事件,内存中不保存任何内容。所以,如果文档很小、数据读取模式很复杂,DOM就是很好的选择,不然,就使用SAX。
不过,实际情况从来没有这么简单。开发人员之所以往往不愿意使用SAX,原因是它很复杂,不过又只好使用它,因为没有其他切实可行的选择。不然,如果XML文件超过几百KB,DOM的内存开销和性能下降对应用开发人员来说就会成为棘手的绊脚石,这样就无法满足项目对性能的最低要求。
但SAX果真好得多吗?SAX宣称的解析性能(通常比DOM快好几倍)实际上往往带有欺骗性。事实证明,SAX解析具有的笨拙、只向前(forward-only)的性质不但要求额外的实现工作,而且只要文档结构变得稍稍复杂,性能就会受到影响。如果开发人员决定不对文档进行多次扫描,就必须缓存文档,或者创建定制的对象模型。
不管怎样,性能都会受到影响,尽管可以通过内部使用SAX来创建更高性能的实现方案,可它仍要创建自己的对象模型,该模型酷似DOM,结果与其前身(Apache SOAP)相比,性能几乎没什么提升。此外,SAX与XPath的兼容性也不是很好,通常无法驱动可扩展样式表语言转换(XSLT)处理。所以,SAX解析绕开了XML处理存在的重大问题。
在寻求比SAX更易使用的替代方案的过程中,越来越多的开发人员求助于用XML的流式API(StAX)。与SAX相比,StAX解析器可以从XML文件拉取标记,而不是使用回调。虽然它们会显著提高易用性,但还是存在根本问题——StAX的只向前解析方式仍需要繁琐的实现工作以及伴随而来的隐性性能成本。
因此,任何XML处理模型要具有广泛用途,必须完整呈现XML的层次结构。原因在于,由于XML被设计成通过Web传送复杂数据,所以传送结构信息是XML固有的一项功能。
VTD-XML改变游戏规则
假设我们从头开始使用XML处理来克服DOM和SAX存在的上述问题。新模型可能应当具有以下特性:
● 随机读取功能: 处理模型应当让开发人员可以人工或者使用XPath浏览某种层次结构;
● 高性能: 性能应当远高于DOM和SAX,而性能应当是“切实可靠的”,这意味着性能评测必须包括用于构建层次结构的时间;
● 内存使用少: 为了让处理模型适用于一系列广泛的场合及文件大小,它必须呈现XML的全部结构,同时内存使用数量最少。
为了满足这些目标而设计的VTD-XML是下一代开放源代码XML处理模型,与DOM和SAX相比,它作了根本、全面的改进。VTD-XML进行优化的一个重要方面就是非提取式标记化(non-extractive tokenization)。在内部,VTD-XML把原封不动、未解码的XML消息保存在内存中,完全根据名为虚拟标记描述符(VTD)的二进制编码规范来呈现标记。VTD记录是64位整数,它用XML对标记长度、标记的起始偏移、类型和嵌套深度进行了编码。
关于VTD-XML,最初提出的基本目的是基于专用硬件来移植XML处理,从而使网络交换机和路由器能够高速处理XML内容。这种专用硬件具体采用现场可编程门阵列(FPGA)或者专用集成电路(ASIC)。后来,VTD-XML项目队伍决定开放VTD-XML的源代码,2004年5月推出了初期版本: 版本0.5,用Java来实现。自该版本以后,VTD-XML经历了好几轮的改进,因而大大成熟。在版本0.8中,C语言版本VTD-XML和Java版本VTD-XML一同发布。版本1.0引入了内置的XPath支持功能,于2005年10月发布。最新版本: 版本1.5采用了经过改写的解析引擎,该引擎模块化程度更高、性能更强。
该版本另外还引入了名为缓冲器重用(buffer reuse)的一项特性。基本思想是,如果位于网络连接后面的XML应用需要重复处理许多进来的XML文档,应用实际上重复使用在第一次处理过程中分配的内存缓冲器。换句话说,缓冲器分配一次,可以使用多次。VTD-XML所特有的这项特性完全消除了XML处理引起的对象创建及废料收集成本(占DOM和SAX开销的50%到80%)。VTD-XML项目网站包括最新软件下载及VTD-XML的详细技术介绍。
快速示例
为了感受VTD-XML的编程风格,本文对使用VTD-XML和DOM的代码进行了比较,代码用来解析及浏览简单的XML文件: test.xml,该文件的文本内容显示如下:
< purchaseOrder orderDate="1999-10-21"> < item partNum="872-AA"> < productName>Lawnmower< /productName> < quantity>1< /quantity> < USPrice>148.95< /USPrice> < /item> < /purchaseOrder> |
VTD-XML版本样式如下:
import com.ximpleware.*; import com.ximpleware.parser.*; import java.io.*; public class use_vtd { public static void main(String[] args){ try{ File f = new File("test.xml"); FileInputStream fis = new FileInputStream(f); byte[] ba = new byte[(int)f.length()]; fis.read(ba); VTDGen vg = new VTDGen(); vg.setDoc(ba); vg.parse(false); VTDNav vn = vg.getNav(); if (vn.matchElement("purchaseOrder")){ System.out.println(" orderDate==>" + vn.toString(vn.getAttrVal("orderDate"))); if (vn.toElement(VTDNav.FIRST_CHILD,"item")){ if (vn.toElement(VTDNav.FIRST_CHILD)){ do { System.out.print( vn.toString(vn.getCurrentIndex())); System.out.print("==>"); System.out.println( vn.toString(vn.getText())); } while(vn.toElement(VTDNav.NEXT_SIBLING)); } } } } catch (Exception e){ System.out.println("exception occurred ==>"+e); } } } |
同一应用的DOM版本样式如下:
import java.io.*; import org.w3c.dom.*; import org.w3c.*; import javax.xml.parsers.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.*; import org.xml.sax.SAXException; public class use_dom { public static void main(String[] args){ try{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = factory. newDocumentBuilder DocumentBuilder(); Document d= parser.parse("test.xml"); Element root = d.getDocumentElement(); if (root.getNodeName().compareTo("purchaseOrder")==0){ System.out.println(" orderDate==> " + root.getAttribute("orderDate")); Node n = root.getFirstChild(); if (n != null){ do { if (n.getNodeType() == Node.ELEMENT_NODE && n.getNodeName().compareTo("item")==0){ Node n2 = n.getFirstChild(); if (n2!=null){ do{ if (n2.getNodeType() == Node.ELEMENT_NODE){ System.out.println( n2.getNodeName() + "==>" + n2.getFirstChild().getNodeValue() ); } }while((n2=n2.getNextSibling())!=null); } } }while ((n=n.getNextSibling()) != null ); } } } catch (Exception e){ System.out.println("exception occurred ==>"+e); } } } |
正如上面的代码示例所示,VTD-XML使用基于游标的API浏览XML层次结构。相比之下,DOM API通过请求对象引用来浏览层次结构。访问VTD-XML项目网站,即可参阅更详细的技术资源和代码示例,上面详细解释了VTD-XML。
VTD-XML的基准测量
接下来,我们把VTD-XML和一些流行的XML解析器就性能和内存使用方面作一比较。
测试环境:测试平台是索尼VAIO笔记本电脑,配置了奔腾M 1.7GHz处理器(2 MB集成二级高速缓存)和512 MB DDR2 RAM。前端总线的时钟频率为400 MHz; 操作系统采用服务包(SP)2的Windows XP专业版; 虚拟机(JVM)采用版本1.5.0_06。
基准测试了最新版本的下列XML解析器:
● Xerces DOM 2.7.1,支持及不支持延迟节点扩展特性
● Xerces SAX 2.7.1
● Piccolo SAX 1.04
● XPP3 1.1.3.4.O
● VTD-XML 1.5,支持及不支持缓冲器重用特性
测试中选择了大小和结构复杂性不同的一大批XML文档进行测试。测试文档根据文件大小分成三类: 小文件大小不到10 KB; 中等大小的文件在10 KB到1 MB之间; 大文件指大于1 MB的文件。
服务器JVM用于所有性能测评,以获得峰值性能。在这些测试中,基准测试程序先执行多次解析或者浏览操作,以便JVM可以对字节码进行动态、及时的优化,之后求得随后几次操作的性能的平均值,作为最终结果。为了减小磁盘I/O操作引起的时序变化,基准测试程序在开始进行测试前把所有XML文件读入到内存中的缓冲器里面。
有兴趣的读者可以在下面的链接中下载基准测试程序: http://sourceforge.net/project/showfiles.php?group_id=110612。