利用VTD-XML剪切、粘贴、分割以及合并XML文档

来源:互联网 发布:极光网络有魔戒世界吗 编辑:程序博客网 时间:2024/06/09 23:14

利用VTD-XML剪切、粘贴、分割以及合并XML文档.

——VTD-XML解决了涉及到更新xml文档时遇到的问题

作者:Jimmy Zhang, JavaWorld.com, 07/24/06

翻译:笨小孩12/15/06

我在javaWorld上次发表的文章:"Simplify XML Processing with VTD-XML"VTD-XML简化XML处理,文中着重于讲解VTD-XML了三大重要的优点:性能,内存占用和易用性.VTD-XML使得XMl程序更容易编写和学习.VTD-XML编写的XML应用程序有比用Document Object Model (DOM) 编写的相同的应用程序十余倍的响应速度,当它们用于维护同样比例的XML消息的服务请求时,VTD-XML编写的XML应用程序可以负载十余倍用Document Object Model (DOM) 编写的相同的应用程序所能负载的工作量.

然而,一般情况下解析文档只是基于XML的应用程序所要作的工作的一部分;许多应用程序也可以更改XML数据.考虑以下的三种涉及到更新XML文档内容的用例:

    1. 一个地址薄程序在程序内部把数据保存为XMl格式,而用户想要更改联系信息(比如:一个电话号码)

      2. 一个支持XML的内容打开检查来往的SOAP信息的开关,并且有选择的打开SOAP头文件中的mustUnderstand属性

      3.一个SOA (service-oriented architecture)安全程序规范了一个用来在后面标记和加密的XMl数据的子集,然后将摘要和密码插回XML有效载荷.

  对于这些用例,有效的更改XML内容的能力对整体的程序性能的高低有着重要的影响.但是就像我在下面将要讲到的,利用VTD-XML的增量更新特性,通常DOMSAX所涉及到的性能问题将会得以消除.

 DOM或者SAX更新XML文档时的双重打击

不幸的是,这些改变应用到XML,DOM或者SAX(处理XML简单的API)的程序性能表现的更加低下了.首先,解析文档时非常慢并且占用了过多的cpu资源,甚至生了成新的XMl文档时还需要反序列化.考虑将下面的XML片段将文本内容的“red”更改为“blue.

<color> red </color>

如果用DOM的话,你得按照以下三个步骤来实现:

     1.建立DOM

       2.遍历然后修改节点

       3.将修改后的结构写回XML

SAXPULL则更不值得在这里提了,它们甚至不支持遍历树结构.如果XMl文档的大小增加,将更新写入到文档中——高效的,字符串关联,缓存分配以及编码转换——这更降低了本已由于解析慢而受到影响的整体程序性能.

然而,我们却可以用一个文本编辑器更加高效的完成同样的测试.要编辑上面例子中的XML片段我们只需用记事本打开文档,将“red”替换为“bule,这就完成了!请注意,这次更新是“增量”更新的,没有涉及到文档中不相干的部分.既然我们自己都能这样编辑XML文档,为什么一个XML解析器去不能呢?

VTD-XML支持增量更新

VTD-XML是第一个支持普通解析和增量更新的XML解析器.即:VTD-XML不但可以快速的解析XML文档,同时它使得零耗更新XML文档内容成为可能,这使它远远超越了DOMSAX成为了一个高级并且功能强大的XML解析器.

VTD-XML是这样完成所以的这些功能呢?它是通过解决了三个对于DOMSAX来说根深蒂固的问题,这些问题中的任何一个都可以说是对性能的一次迫害.首先是对象定位和和垃圾回收.每次一个对象(比如:DOM节点)都被分配了内存,但它最终会失效并被回收.接下来这个问题实际上所有的文本解析器都存在,以往,文字处理的第一步步往往是将输入的文本用记号分解,这些极小的不连续的字符串中含有相关的文本数据.任何对原始文本的更改都要求将这些标记一起放回到原处.最后一个问题是字符编码和重编码.在解析过程中,DOMSAXXML的编码从本地编码(比如:UTF-8)解码为UCS格式,然而当文档的任何一处内容发生变化时,它有得重编码为本地格式.

VTD-XML解决了所有的这些问题.VTD-XML没有编码的转换,没有不连续的字符串,实际上也没有对象的分配.虚拟标记符(VTD)是"non-extractive" 标记化技术的名字,很大程度上它是VTD-XML高效的关键.VTD记录将XML中的长度、起始偏移量、类型和标记的嵌套深度用64位编码.换句话说,如果解析深度稍大一点,你会发现旧的、基于离散字符串的标记化技术却得做大量的工作;而VTD-XML,你只需用偏移量和长度(通常被称为"non-extractive"标记化)来代表那些标记.

简单来说,VTD-XML剔除了DOMSAX的每一个陋习.下面是对VTD-XML在内存占用、性能和增量更新方面所表现出的不同的一个概要.

节省内存

1.因为VTD记录并不是对象,它们不会出现像java对象(一个对象8bytes)中的逐对象内存开销的现象.

2.VTD存储空间可以整块的分配(例如:大的内存块):当分配了一个大内存块来存储1024VTD标记,每个标记只需消耗一次内存开销。这从本质上将按记录开销内存的情况消除了。

3.可以很容易的再利用VTD标记的缓存.

高性能

1.VTD-XML的高解析性能是VTD的内存节省特性的一个副产品:低内存损耗意味着更少的内存被分配.

2.大的内存块对于内存分配和垃圾回收的速度比许多离散对象要快.

增量更新

因为VTD-XML使得原始XML成为它内部表示的一部分并且用它独特的偏移量和长度表示那些标记,这样即可以非常高的精度来更新XML内容并且不会再浪费任何的CPU资源.

这里有个小例子可以帮助你熟悉增量更新这个概念,如下图所示,我们用VTD-XML来替换XML文件中的一个属性值.

替换一个属性值

为了用同样的方法得到新的XMl文档,你得用一个文本编辑器进行同样的修改,我们的程序(源代码在下面)首先用UTDGen解析test.xml;它的parseFile()方法将指针置于那个属性的位置,用一个新值替换旧值(图中用绿色标记),然后马上将更改了的文档写回upated.xml

import com.ximpleware.*;

import java.io.*;
public class increUpdate{

    public static void main(String[] args){

       try{

          VTDGen vg = new VTDGen();

          File fo = new File("updated.xml");

          FileOutputStream fos = new FileOutputStream(fo);

          if (vg.parseFile("test.xml",false)){

              VTDNav vn = vg.getNav();

              if (vn.matchElement("purchaseOrder")){

                 int i = vn.getAttrVal("orderDate");

                 if (i!=-1){

                    //Get the starting offset of "1999-10-21"

                    int os1 = vn.getTokenOffset(i);

                    // Get the ending offset of "1999-10-21"

                    int os2 = vn.getTokenOffset(i)+ vn.getTokenLength(i);

                    // Get the total number of bytes of XML

                    int os3 = vn.getXML().length() ;

                    byte[] xml = vn.getXML().getBytes();
                    // Write everything before "1999-10-21"

                    fos.write(xml, 0, os1);

                    // Write "2006-6-17"

                    fos.write("2006-6-17".getBytes());

                    // Write everything after

                    fos.write(xml, os2 , os3 - os2);

                    fos.close();
                 }

              }

           }

       }

       catch (Exception e){

          System.out.println("exception occurred ==>"+e);

       }

    }

}

 

VTD-XML处理文档内容

VTD-XML不仅支持增量更新,它的non-extractive标记化还包括其他一系列的新特性.下面列出的四个对XML文档内容进行的操纵都是可以通过VTD-XML实现的.

    剪切:对于一个XML文档,在保证它原有良好的组织形式的前提下,取走文档的一部分内容(例如:一个属性,一个文字节点或者一个元素)

    粘贴:拷贝了一块XML文档后,在保证它原有良好的组织形式的前提下,将它粘贴到另一份XML文档中.

    分割XML一个迷人的地方就是根节点的任何一个元素都是一个XML.VTD-XML可以剪切一大块单独的XMl文档中元素分别形成单独的新的XMl文档.

    合并:将零碎的元素片段合并为一份新的XML文件.

 

API的级别上,VTD-XML支持在如下三个层面上执行这些操作:

    单个标记:只要你有VTD记录的序列号,你就可以用VTDNavgetTokenOffset(...)  getTokenLength(...)方法来返回标记的分支和长度.当你要更新文档时就会用到这俩个值.

     一组相近的标记:有时,一组相近的标记(属性名和属性值)必须一次被替换或删除.在这种情况下,你得分别获取到组里第一个和最后一个标记的起始偏移量.

     一个元素VTDNavgetElementFragment(...) 方法返回一个元素在XML文档中的偏移量和长度.这样你就可以用字节级的精度直接操作元素的内容.

 

这里是下面例子中将要用到的一些基本的VTD-XML概念

    基本类:

    1.VTDGen是执行解析功能的类的名字

    2.解析后,你可以得到VTDGen的一个实例,用它可以遍历整颗树.

    3.AutoPilot是是XPath和节点遍历的包装类

     基于指针的模型:仅有一个指针是可能的.解析后,指针位于根节点.你可以用一个全局堆栈来记下这个指针的位置.

     无状态XPath估算:除非节点是空的,否则VTD-XMXpath赋值一次将返回一个节点.AutoPilot类的实例像一只魔术手,它通过XPath表达式在XMl树中移动指针.

剪切 XMl

我们的第一个例子是删除一个如下图所示的名为cd.xmlCD目录文件的部分内容.请注意:其中两张CD的价格是低于10,而其余的两张的价格是大于10.

<CATALOG>

    <CD>

        <TITLE>Empire Burlesque</TITLE>

        <ARTIST>Bob Dylan</ARTIST>

        <COUNTRY>USA</COUNTRY>

        <COMPANY>Columbia</COMPANY>

        <PRICE>10.90</PRICE>

        <YEAR>1985</YEAR>

     </CD>

     <CD>

        <TITLE>Still got the blues</TITLE>

        <ARTIST>Gary More</ARTIST>

        <COUNTRY>UK</COUNTRY>

        <COMPANY>Virgin redords</COMPANY>

        <PRICE>10.20</PRICE>

        <YEAR>1990</YEAR>

     </CD>

     <CD>

        <TITLE>Hide your heart</TITLE>

        <ARTIST>Bonnie Tyler</ARTIST>

        <COUNTRY>UK</COUNTRY>

        <COMPANY>CBS Records</COMPANY>

        <PRICE>9.90</PRICE>

        <YEAR>1988</YEAR>

     </CD>

     <CD>

        <TITLE>Greatest Hits</TITLE>

        <ARTIST>Dolly Parton</ARTIST>

        <COUNTRY>USA</COUNTRY>

        <COMPANY>RCA</COMPANY>

        <PRICE>9.90</PRICE>

        <YEAR>1982</YEAR>

     </CD>

</CATALOG>

下面的第一个查询是从XML文件中剔除价格大于零的CD.对应的XPath表达式是/CATALOG/CD [PRICE > 10].主程序逻辑首先实例化VTDGen来解析XML文件,然后实例化AutoPilot并且取出XPath表达式.主程序在XPath赋值期间重复调用getElementFragment(),这个方法返回对一个包含了匹配XPath的元素的内容片段的偏移量和长度编号后的long.最后一步,程序拷贝了从原始文件的字节码中减去那些被选中的片段并新建一个名为cd_after.xml的文件.

import com.ximpleware.*;

import java.io.*;

// This example shows how to delete all CDs priced above 0

public class cut {

  public static void main(String[] args){

    try{

        VTDGen vg = new VTDGen();

        File fo = new File("cd_after.xml");

        FileOutputStream fos = new FileOutputStream(fo);

        if (vg.parseFile("cd.xml",false)){

            VTDNav vn = vg.getNav();

            AutoPilot ap = new AutoPilot(vn);

            ap.selectXPath("/CATALOG/CD[PRICE > 10]");

            // flb contains all the offset and length of the segments to be skipped

            FastLongBuffer flb = new FastLongBuffer(4); // Page size is 2^4 = 16

            int i;

            byte[] xml = vn.getXML().getBytes();

            while( (i=ap.evalXPath())!= -1){

               flb.append(vn.getElementFragment());

            }

            int size = flb.size();

            if (size == 0){

                fos.write(xml); // No change needed because no CD is above 0

            }

            else{

               int os1 = 0;

               for (int k = 0;k<size; k++){

                   fos.write(xml, os1, flb.lower32At(k)-1 - os1);

                   os1 = flb.upper32At(k) + flb.lower32At(k);

               }

               fos.write(xml, os1, xml.length - os1);

           }

        }

    }

    catch (Exception e){

        System.out.println("exception occurred ==>"+e);

    }

  }

}

下面是名为cd_after.xml的输出文件的内容:

<CATALOG>
    <CD>

        <TITLE>Hide your heart</TITLE>

        <ARTIST>Bonnie Tyler</ARTIST>

        <COUNTRY>UK</COUNTRY>

        <COMPANY>CBS Records</COMPANY>

        <PRICE>9.90</PRICE>

      <YEAR>1988</YEAR>

    </CD>

    <CD>

        <TITLE>Greatest Hits</TITLE>

        <ARTIST>Dolly Parton</ARTIST>

        <COUNTRY>USA</COUNTRY>

        <COMPANY>RCA</COMPANY>

        <PRICE>9.90</PRICE>

        <YEAR>1982</YEAR>

    </CD>

    <CD>

        <TITLE>Sylvias Mother</TITLE>

        <ARTIST>Dr.Hook</ARTIST>

        <COUNTRY>UK</COUNTRY>

        <COMPANY>CBS</COMPANY>

        <PRICE>8.10</PRICE>

        <YEAR>1973</YEAR>

    </CD>

    <CD>

        <TITLE>When a man loves a woman</TITLE>

        <ARTIST>Percy Sledge</ARTIST>

        <COUNTRY>USA</COUNTRY>

        <COMPANY>Atlantic</COMPANY>

        <PRICE>8.70</PRICE>

        <YEAR>1987</YEAR>

</CD>
</result>

你可以从资源下载本文示例的代码.

综述

通过上面演示的VTD-XML增量更新功能以及它剪切、粘贴、分割、和合并XML文档的能力,希望这篇文章能使你更进一步的理解到为什么VTD-XML将会是下一代的XML处理API,虽然实际上DOM SAX也是有肯能的.对于上面说到的non-extractive 标记化,你可能想知道为什么不早点应用在文字处理上呢?文章的余下的部分我会概括一些我关于VTD-XML的过去、现在和将来的想法,以及在XML界的一些发展.

问题已经改变

DOMSAX都是基于几年前为编译器发明的传统的文字处理技术,因为它只是用来产生二进制代码,比如可执行文件或者库文件,所以它性能要求并不高.

用到DOM HTML-rendering同意不要求高性能,应为对于用户的感受来说, 10ms200ms是没有太大的差别的.在这样一个环境中:一个持续的XML流要被解析、更新并且执行实时的查询,DOM,SAX和它们对应的文字处理方式最终将表现出劣势并为像VTD-XML这样的新技术的创新让路.

不要低估坏标准的危害

     尽管DOMSAX被广泛的采用,但有一点却不是很明确:在为企业实现XMl的商业利润方面,它们是否功大于过呢? 你想过在过去的八年里为什么任何的DOM实现都没有在内存占用和性能上有显著的提高呢?事实是没有谁能使得DOM运行的更快.DOM规范实际上是基于XML的拱形结构完全由暴露了节点接口的对象组成这样的一个假设的.任何DOM的实现可以做的就是改变节点接口后的对象的实现,而这却不可避免的会导致过多的对象的分配.

SAX则更差劲了,CSV之类的无结构格式相比较,SAX加入了结构化的概念,这使XML向前迈了一大步.然而它却强迫你像对待CSV那样对待XML并要忍受因此带来的不便,这又使SAX倒退了一大步.

现在的Web服务栈上这种危害更加明显,比如和XML签名和加密相关联的.坏标准的共同点是什么呢?它们限制了你的查询性能,实际上它们保证会使你达不到下一个SOA项目的性能要求.你只需明确知道什么时候该和它们说不!

简化事务

XML之所有广受欢迎是因为它非常的简单.那为什么还需要一页一页的看文档去学习如何用DOMSAX来编写XML程序呢?为什么搞得这么复杂呢?还记得DCOM  CORBA的失败吗?让我们把事务简化,简易的事务一般要比复杂工作的好.VTD-XML如此好的表现正式因为他的简易性.

XML没有性能问题

对于XMl人们经常提及的是它的长度和性能.虽然XML的确比CSV更占用空间,XML却具有自描述性并更具有扩展性.XML慢吗?不,从来都不.VTD-XML就是最好的证明.性能问题只有在DOMSAX中才会出现,换句话说所谓的"XML 代码膨胀"用来描述DOM的对象膨胀或者SAX的代码膨胀会更合适一点.但这和XML本身是没有任何关系的.

XML面向对象的表示法是个问题

那么为什么DOMSAX 这么慢呢?很明显,即使人们在XMl模型中用到了对象,但他们也意识到分配大量的对象而导致的对性能的影响.

同样的XML当作序列化了的Java对象(像XMLjava绑定)来对待会遇到难以克服的性能问题,然而这也是件好事,它使我们意识到XML/Web服务的松耦合原则正好说明XML不应单纯的被当作java对象的序列号来对待.

最好的尚未来临

作为一个超越DOMSAX的有用的高级选择,VTD-XML已经向着正确的方向迈了好几步.但是将来变的更强大才是更重要的.VTD-XML有着许多首创的特性,这些之前从未想过的特性将XML工作效率提升到一个新的高度.请拭目以待.

作者简介

Jimmy ZhangXimpleWare的创始人,XimpleWare是一个解决XML处理的高性能方案.有自动化电子设计和voice-over IP经验,并且在硅谷有许多技术伙伴.毕业于伯克利的加利福尼亚大学,并取得了EECS学院的理科硕士和理学士的双学位.

资源

·     下载文中例子的代码

http://www.ximpleware.com/code.zip

·     VTD-XML

http://VTD-XML.sf.net/

·     VTD+XML

http://VTD-XML.sf.net/persistence.html

·     "Simplify XML Processing with VTD-XML," Jimmy Zhang (JavaWorld, March 2006)

http://www.javaworld.com/javaworld/jw-03-2006/jw-0327-simplify.html

·     关于SOAs的更多文章请浏览JavaWorldWeb Services and SOAs 版块

http://www.javaworld.com/channel_content/jw-webserv-index.shtml?

·     更多关于XML的文章请浏览JavaWorldJava and XML版块

  http://www.javaworld.com/channel_content/jw-xml-index.shtml