XStream

 

使用XStream的初衷

 

研究和使用XStream的原因是我在項(xiàng)目中的一個(gè)預(yù)研。在項(xiàng)目中需要應(yīng)用到對(duì)XML文件的管理和配置,因此需要一個(gè)能夠?qū)?duì)象保存為XML的工具庫,在這里有多種方法實(shí)現(xiàn),我也研究并進(jìn)行了比對(duì),比如與Zeus工具的比對(duì),與Java自身的XML工具庫的比對(duì)等。在這里,我就描述下我的XStream學(xué)習(xí)過程和研究結(jié)果。

 

XStream簡(jiǎn)單介紹

 

XStream是一個(gè)開源項(xiàng)目,一套簡(jiǎn)單實(shí)用的類庫,用于序列化對(duì)象與XML對(duì)象之間的相互轉(zhuǎn)換。將XML文件內(nèi)容解析為一個(gè)對(duì)象或?qū)⒁粋€(gè)對(duì)象序列化為XML文件。

XStream可以用于JDK1.3以上的版本使用,我是在JDK1.5下使用它的。

XStream的相關(guān)信息可以到http://xstream.codehaus.org/下查看,它有專門的JavaDoc,可以方便的閱讀Xstream的函數(shù)及方法。

XStream中主要的類為XStream,它用于序列化對(duì)象與XML 對(duì)象之間的相互轉(zhuǎn)換。簡(jiǎn)單的使用它就可以解決很多問題。

XStream中主要的方法也是我用的比較多的是fromXML()toXML()

fromXML用于從XML中將對(duì)象解析出來。

toXML用于將對(duì)象序列化為XML文件。

XStream中我還使用HierarchicalStreamWriterHierarchicalStreamReadercreateObjectInputStream()createObjectOutputStream(),主要是用于對(duì)象的輸入輸出。

下面我們來研究下XStream的工作方式。

 

XStream的實(shí)例——將一個(gè)序列化對(duì)象轉(zhuǎn)化為XML對(duì)象。

 

一,創(chuàng)建XStream對(duì)象。

XStream xstream=new XStream();

用默認(rèn)構(gòu)造器構(gòu)造了一個(gè)名為xstreamXStream的對(duì)象。默認(rèn)構(gòu)造器所使用XML解析庫為Xpp3庫,XPP3是一種運(yùn)行效率非常高的XML全解析實(shí)現(xiàn)。

 

二,創(chuàng)建需要序列化的對(duì)象。

比如這個(gè)類就叫PrintUnit

構(gòu)造也比較簡(jiǎn)單,一個(gè)簡(jiǎn)單的JavaBean

       public class PrintUnit

       {

              Private String a;

              Private String b;

              Private String c;

             

              Public PrintUnit(){}

             

              Public setA(String a)

              {

                     this.a=a;

              }

 

              Public getA()

              {

                     return a;

              }

 

              Public setB(String b)

              {

                     this.b=b;

              }

 

              Public getB()

              {

                     return b;

              }

 

              Public setC(String c)

              {

                     This.c=c;

              }

 

              Public getC()

              {

                     Return c;

              }

       }

 

在例子中使用這個(gè)JavaBean

創(chuàng)建并初始化PrintUnit

PrintUnit pu=new PrintUnit();

pu.setA("A11");

pu.setB("B22");

pu.setC("C33");

 

三,創(chuàng)建Writer

創(chuàng)建一個(gè)輸出流,至于怎么輸出我發(fā)現(xiàn)可以使用多種方法,其實(shí)原理是一樣的。

在這里就不得不提到HierarchicalStreamWriter,HierarchicalStreamWriter是一個(gè)接口,從字面上意思來說它是有等級(jí)的輸入流。同樣在XStream中也有不少這個(gè)接口的實(shí)現(xiàn)類用于輸出。我現(xiàn)在所用過的有CompactWriterPrettyPrintWriter2個(gè)。

我是這樣做的:

String str="stream.xml"; //本目錄下的一個(gè)名為streamXML文件

PrintWriter pw=new PrintWriter(str);//創(chuàng)建一個(gè)PrintWriter對(duì)象,用于輸出。

之后選用一個(gè)HierarchicalStreamWriter的實(shí)現(xiàn)類來創(chuàng)建輸出。

選用CompactWriter創(chuàng)建:

CompactWriter cw=new CompactWriter(pw);

選用PrettyPrintWriter創(chuàng)建:

PrettyPrintWriter ppw=new PrettyPrintWriter(pw);

兩者所使用的方法都是很簡(jiǎn)單的。

CompactWriterPrettyPrintWriter的區(qū)別在于,以CompactWriter方法輸出的為連續(xù)的沒有分隔的XML文件,而用PrettyPrintWriter方法輸出的為有分隔有一定格式的XML文件。

CompactWriter方式生成的XML文件:

 

<object-stream><PrintUnit><a>A11</a><b>B22</b><c>C33</c></PrintUnit></object-stream>

 

PrettyPrintWriter方式生成的XML文件:

       <object-stream>

             <PrintUnit>

                  <a>A11</a>

                  <b>B22</b>

                  <c>C33</c>

             </PrintUnit>

       </object-stream>

 

       我想大家能很容易的分辨出它們的差異。

 

       四,輸出操作      

以上步驟完成后就可以做輸出操作了,XStream的輸出方式有多種:toXML方式,ObjectOutputStream方式,marshal方式以及一些我尚未發(fā)現(xiàn)的一些其它方式。

先說下我所使用的方式它們各自的不同點(diǎn),從工作原理上說它們是相似的,但是做法各不相同。

toXML()方法,本身toXML的方法就有2種:

第一種:java.lang.String toXML(java.lang.Object obj)

將對(duì)象序列化為XML格式并保存到一個(gè)String對(duì)象中。

第二種:void toXML(java.lang.Object obj, java.io.Writer out)

將對(duì)象序列化為XML格式后以Writer輸出到某個(gè)地方存儲(chǔ)。

我所使用的是第二種方式,使用前面已經(jīng)做好的Pw就可以實(shí)現(xiàn)輸出,它其實(shí)很簡(jiǎn)單不需要再去做其它定義,只需要一個(gè)PrintWriter對(duì)象和需要序列化的Object即可。

直接調(diào)用xstream.toXML(printUnit,pw);就能輸出XML文件,在這里是輸出到該目錄下的stream.xml中。這里的輸出都是覆蓋性的,不是末尾添加形式。

使用ObjectOutputStream方式,簡(jiǎn)單說它就是生成一個(gè)對(duì)象輸出流。

ObjectOutputStream obj_out = xstream.createObjectOutputStream(ppw);

使用XStreamcreateObjectOutputStream方法創(chuàng)建一個(gè)ObjectOutputStream對(duì)象,用于XML的輸出。這里使用的是PrettyPrintWriter的方式。   之后調(diào)用writerObject方法既可,使用方法與其它輸出流類似。

obj_out.writeObject(pu);

obj_out.close();

使用marshal方式,其實(shí)marshal方法和toXML方法是相同的。在調(diào)用toXML方法進(jìn)行輸出時(shí),在XStream內(nèi)部是需要調(diào)用marshal方法的,然后它再去調(diào)用對(duì)象marshallingStrategymarshal方法。所以做toXML其實(shí)和marshal是相同的,在這里只是想更加說明它的工作方式。

 

使用 void marshal(java.lang.Object obj, HierarchicalStreamWriter writer)方法。

延續(xù)上面的例子,在這里可以這樣寫:xstream.marshal(pu,ppw);

 

需要注意的是,和toXML不同的是參數(shù),一個(gè)是PrintWriter對(duì)象一個(gè)則是PrettyPrintWriter對(duì)象。因?yàn)?/SPAN>marshal中需要

HierarchicalStreamWriter,而PrettyPrintWriter則是實(shí)現(xiàn)了HierarchicalStreamWriter接口的實(shí)現(xiàn)類。

 

結(jié)果和toXML是相同的。

 

五,結(jié)果:

       <object-stream>

             <PrintUnit>

                  <a>A11</a>

                  <b>B22</b>

                  <c>C33</c>

             </PrintUnit>

       </object-stream>

 

經(jīng)過以上5步的操作既可將一個(gè)序列化對(duì)象轉(zhuǎn)化為XML對(duì)象。

 

 

toXML內(nèi)部調(diào)用圖:

 

XStream.giftoXML操作時(shí)的內(nèi)部調(diào)用圖,自己隨意畫的。有些沒有詳細(xì)說明。

 

 

XStream的實(shí)例——將XML文件轉(zhuǎn)化為一個(gè)對(duì)象

 

通過上面的一個(gè)例子不難看出XStream簡(jiǎn)便性,既然有了輸出就一定會(huì)有輸入。

輸入方我們將會(huì)使用ObjectInputStream

與輸出相同我們需要有一個(gè)XStream對(duì)象,暫且名為xstream。之后需要讀取的XML文件地址目錄信息。沿用上面的例子。

String inputStr="xstream.xml";

XStream xstream=new XStream();

我們需要通過對(duì)象流進(jìn)行輸入操作,所以需要FileReaderBufferedReader

FileReader fr=new FileReader(inputStr);

BufferedReader br=new BufferedReader(fr);

創(chuàng)建對(duì)象輸入流

ObjectInputStream obj_input=xstream.createObjectInputStream(br);

創(chuàng)建對(duì)象,還是使用PrintUnit這個(gè)對(duì)象。

PrintUnit pu2;

通過ObjectInputStream中的readObject()方法將對(duì)象從XML文件中讀取出來。

pu2=(PrintUnit)obj_input.readObject();

獲取值:

System.out.println(pu2.getB());

控制臺(tái):

B22 

 

從整個(gè)輸入的過程來看,是一個(gè)文件的讀取,將其中的對(duì)象數(shù)據(jù)取出來,然后再對(duì)這個(gè)對(duì)象數(shù)據(jù)進(jìn)行操作。內(nèi)容也比較簡(jiǎn)單通過ObjectInputStream輸入對(duì)象。

通過以上的輸入輸出例子,我想大家應(yīng)該很容易就能理解XStream是如何實(shí)現(xiàn)的。

 

FomXML

 

上面使用的是以ObjectInputStream的方式進(jìn)行XML與對(duì)象之間進(jìn)行轉(zhuǎn)換的。下面我將使用XStream中的fromXML()方法進(jìn)行轉(zhuǎn)換。

首先在使用fromXML我發(fā)現(xiàn)一個(gè)問題,它必須使用正確的解析方式或輸出方式對(duì)應(yīng)的輸入方式才可以正常解析讀取文件,這個(gè)問題有點(diǎn)怪,不過確實(shí)存在,當(dāng)我使用前面ObjectOutputStream方式輸出的XML文件,fromXML()解析讀取時(shí),它會(huì)報(bào)錯(cuò)。

錯(cuò)誤信息:

Exception in thread "main" com.thoughtworks.xstream.alias.CannotResolveClassException: object$stream : object$stream

信息內(nèi)容為:不能解析這個(gè)文件。我認(rèn)為它和輸出方式有關(guān),因?yàn)樯厦胬又惺褂玫氖?/SPAN>ObjectOutputStream,當(dāng)我反過來做了一個(gè)實(shí)驗(yàn)后也證明了這一點(diǎn)。

實(shí)驗(yàn)大致內(nèi)容:使用toXML()方法輸出XML文件,使用ObjectInputStream解析,發(fā)現(xiàn)會(huì)在讀取的時(shí)候拋出CannotResolveClassException異常。

錯(cuò)誤信息:

Exception in thread "main" com.thoughtworks.xstream.alias.CannotResolveClassException:

a : a

       因此我認(rèn)為在解析文件的時(shí)候必須先要確定這個(gè)文件是由什么方式生成的,然后在解析它,對(duì)于使用Dom,Dom4j,XPP等不同方式解析尚未嘗試。以上測(cè)試是在默認(rèn)的基礎(chǔ)上實(shí)驗(yàn)的,默認(rèn)為XPP3的解析器。

 

       使用fromXML的方法。

      

       public java.lang.Object fromXML(java.lang.String xml)

       public java.lang.Object fromXML(java.io.Reader xml)

public java.lang.Object fromXML(java.lang.String xml,java.lang.Object root)

public java.lang.Object fromXML(java.io.Reader xml,java.lang.Object root)

      

例子:

       PrintUnit puTwo=(PrintUnit)xstream.fromXML(xml);

      

這里的xml必須是使用toXML()生成出來的。對(duì)于Reader沒有太多的要求。

 

 

XStreamJava.BeanXML工具的比較

 

       XStream主要作用是將序列化的對(duì)象轉(zhuǎn)化為一個(gè)XML文件或?qū)?/SPAN>XML文件解析為一個(gè)對(duì)象。當(dāng)然并非只有它可以做到,很多其它工具一樣可以,在Java中存在這樣兩個(gè)類XMLDecoderXMLEncoder,它們是在Java.Bean包下的,它們的作用是將JavaBean轉(zhuǎn)化為XML或?qū)?/SPAN>XML文件轉(zhuǎn)化為一個(gè)Java Bean

       XMLDecoder是通過一個(gè)輸入流將對(duì)象從輸入流中取出并轉(zhuǎn)化為一個(gè)實(shí)例的方法。它所需要的就是一個(gè)輸入流及一個(gè)轉(zhuǎn)化過程。

 

       XMLDecoder的實(shí)例:

 

       String fileStr=”xstream.xml”;//XML文件,在本目錄下,延用上次使用文件。

       ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileStr));//創(chuàng)建一個(gè)ObjectInputStream用于輸入。

       XMLDecoder xmld=new XMLDecoder(in);//創(chuàng)建一個(gè)XMLDecoder對(duì)象。

       延用前面所使用PrintUnit這個(gè)Bean

       PrintUnit pu=(PrintUnit)xmld.readObject();//通過XMLDecoder中的readObject方法獲得PrintUnit對(duì)象。

如果獲取到了這個(gè)對(duì)象那么pu中將有它的值a=A11,b=B22,c=C33。整個(gè)過程最好放try

…catch中去,能夠捕獲一些如:文件不存在等異常。

       從操作方式上看XMLDecoder似乎不比XStream差多少,同樣是可以通過ObjectInputStream獲取XML文件中的對(duì)象。它們的差異就是解析的方式不同,XMLDecoder是使用Java自帶的XML解析方式,而XStream則是可以自定義的,它可以使用多中方式進(jìn)行解析。這些是我個(gè)人所發(fā)現(xiàn)的一些不同點(diǎn)。

 

       XMLEncoder是通過一個(gè)輸出流將對(duì)象序列化并輸出為XML文件。它所需要的是一個(gè)輸出流及一個(gè)輸出方式。

 

       XMLEncoder的實(shí)例:

 

       String fileStr=”xstream.xml”;//定義一個(gè)輸入的目標(biāo)文件。

       ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileStr));//創(chuàng)建一個(gè)對(duì)象輸出流。

       XMLEncoder xmle=new XMLEncoder(out);//創(chuàng)建一個(gè)XMLEncoder對(duì)象。

       延用前面所使用PrintUnit這個(gè)Bean

//創(chuàng)建并初始化PrintUnit對(duì)象。

PrintUnit pu=new PrintUnit();

pu.setA(“AAA”);

pu.setB(“BBB”);

pu.setC(“CCC”);

 

       xmle.writeObject(pu);//使用XMLEncodewriteObject方法輸出pu

       xmle.flush();//刷新

       xmle.close();//關(guān)閉輸出流

 

       從上面的代碼不難看出,使用XMLEncode方式將對(duì)象序列化并輸出也是很方便的,簡(jiǎn)單調(diào)用writeObject方法能將普通Bean輸出為XML文件。

      

       XML文件的內(nèi)容:

 

?_ <?xml version="1.0" encoding="UTF-8"?>

<java version="1.5.0" class="java.beans.XMLDecoder">

 <object class="test.PrintUnit">

  <void property="a">

   <string>AAA</string>

  </void>

  <void property="b">

   <string>BBB</string>

  </void>

  <void property="c">

   <string>CCC</string>

  </void>

 </object>

w   </java>

 

       不知道是我哪里沒有處理,還是實(shí)際并不是像我想象的哪么簡(jiǎn)單,使用XMLEncoder所輸出的XML文件中有一定的問題,雖然它很詳細(xì),比起XStream所生成的更多,包括了XMLJava的版本看上去更像是個(gè)完整的XML文件,不過再細(xì)看它們兩生成的XML格式內(nèi)容,完全不同,這個(gè)我想就是它們最大的區(qū)別。這讓我想到了很多內(nèi)容:工作方式,解析器,轉(zhuǎn)換方式等。大家有沒發(fā)現(xiàn)在開始和結(jié)束都存在一些亂碼數(shù)據(jù),難道在XMLEncoder輸出過程中或數(shù)據(jù)轉(zhuǎn)換中內(nèi)容已經(jīng)存在“臟”數(shù)據(jù)了?還是我所使用的輸出方式存在問題?哎一個(gè)又一個(gè)問題出現(xiàn)了。我想我需要再進(jìn)一步的研究和學(xué)習(xí)才能得到答案。

       不過盡管有這個(gè)那個(gè)的問題,使用Java本身自帶的XML工具還是一樣很實(shí)用的,讀取和輸出一樣可用,操作也很靈活。因此我覺得在某些場(chǎng)合使用特定的工具可能會(huì)更好,利用XMLEncoderXMLDecoder同樣可以解決一些問題。

我的這個(gè)使用XMLDecoderXMLEncoder的序列化格式輸出暫研究到這里。

楓情·太子爺
2005年12月16日