XStream

 

使用XStream的初衷

 

研究和使用XStream的原因是我在項目中的一個預研。在項目中需要應用到對XML文件的管理和配置,因此需要一個能夠將對象保存為XML的工具庫,在這里有多種方法實現,我也研究并進行了比對,比如與Zeus工具的比對,與Java自身的XML工具庫的比對等。在這里,我就描述下我的XStream學習過程和研究結果。

 

XStream簡單介紹

 

XStream是一個開源項目,一套簡單實用的類庫,用于序列化對象與XML對象之間的相互轉換。將XML文件內容解析為一個對象或將一個對象序列化為XML文件。

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

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

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

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

fromXML用于從XML中將對象解析出來。

toXML用于將對象序列化為XML文件。

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

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

 

XStream的實例——將一個序列化對象轉化為XML對象。

 

一,創建XStream對象。

XStream xstream=new XStream();

用默認構造器構造了一個名為xstreamXStream的對象。默認構造器所使用XML解析庫為Xpp3庫,XPP3是一種運行效率非常高的XML全解析實現。

 

二,創建需要序列化的對象。

比如這個類就叫PrintUnit。

構造也比較簡單,一個簡單的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;

              }

       }

 

在例子中使用這個JavaBean。

創建并初始化PrintUnit。

PrintUnit pu=new PrintUnit();

pu.setA("A11");

pu.setB("B22");

pu.setC("C33");

 

三,創建Writer。

創建一個輸出流,至于怎么輸出我發現可以使用多種方法,其實原理是一樣的。

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

我是這樣做的:

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

PrintWriter pw=new PrintWriter(str);//創建一個PrintWriter對象,用于輸出。

之后選用一個HierarchicalStreamWriter的實現類來創建輸出。

選用CompactWriter創建:

CompactWriter cw=new CompactWriter(pw);

選用PrettyPrintWriter創建:

PrettyPrintWriter ppw=new PrettyPrintWriter(pw);

兩者所使用的方法都是很簡單的。

CompactWriterPrettyPrintWriter的區別在于,以CompactWriter方法輸出的為連續的沒有分隔的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方式以及一些我尚未發現的一些其它方式。

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

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

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

將對象序列化為XML格式并保存到一個String對象中。

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

將對象序列化為XML格式后以Writer輸出到某個地方存儲。

我所使用的是第二種方式,使用前面已經做好的Pw就可以實現輸出,它其實很簡單不需要再去做其它定義,只需要一個PrintWriter對象和需要序列化的Object即可。

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

使用ObjectOutputStream方式,簡單說它就是生成一個對象輸出流。

ObjectOutputStream obj_out = xstream.createObjectOutputStream(ppw);

使用XStreamcreateObjectOutputStream方法創建一個ObjectOutputStream對象,用于XML的輸出。這里使用的是PrettyPrintWriter的方式。   之后調用writerObject方法既可,使用方法與其它輸出流類似。

obj_out.writeObject(pu);

obj_out.close();

使用marshal方式,其實marshal方法和toXML方法是相同的。在調用toXML方法進行輸出時,在XStream內部是需要調用marshal方法的,然后它再去調用對象marshallingStrategymarshal方法。所以做toXML其實和marshal是相同的,在這里只是想更加說明它的工作方式。

 

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

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

 

需要注意的是,和toXML不同的是參數,一個是PrintWriter對象一個則是PrettyPrintWriter對象。因為marshal中需要

HierarchicalStreamWriter,而PrettyPrintWriter則是實現了HierarchicalStreamWriter接口的實現類。

 

結果和toXML是相同的。

 

五,結果:

       <object-stream>

             <PrintUnit>

                  <a>A11</a>

                  <b>B22</b>

                  <c>C33</c>

             </PrintUnit>

       </object-stream>

 

經過以上5步的操作既可將一個序列化對象轉化為XML對象。

 

 

toXML內部調用圖:

 

XStream.giftoXML操作時的內部調用圖,自己隨意畫的。有些沒有詳細說明。

 

 

XStream的實例——將XML文件轉化為一個對象

 

通過上面的一個例子不難看出XStream簡便性,既然有了輸出就一定會有輸入。

輸入方我們將會使用ObjectInputStream。

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

String inputStr="xstream.xml";

XStream xstream=new XStream();

我們需要通過對象流進行輸入操作,所以需要FileReaderBufferedReader。

FileReader fr=new FileReader(inputStr);

BufferedReader br=new BufferedReader(fr);

創建對象輸入流

ObjectInputStream obj_input=xstream.createObjectInputStream(br);

創建對象,還是使用PrintUnit這個對象。

PrintUnit pu2;

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

pu2=(PrintUnit)obj_input.readObject();

獲取值:

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

控制臺:

B22 

 

從整個輸入的過程來看,是一個文件的讀取,將其中的對象數據取出來,然后再對這個對象數據進行操作。內容也比較簡單通過ObjectInputStream輸入對象。

通過以上的輸入輸出例子,我想大家應該很容易就能理解XStream是如何實現的。

 

FomXML

 

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

首先在使用fromXML我發現一個問題,它必須使用正確的解析方式或輸出方式對應的輸入方式才可以正常解析讀取文件,這個問題有點怪,不過確實存在,當我使用前面ObjectOutputStream方式輸出的XML文件,fromXML()解析讀取時,它會報錯。

錯誤信息:

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

信息內容為:不能解析這個文件。我認為它和輸出方式有關,因為上面例子中使用的是ObjectOutputStream,當我反過來做了一個實驗后也證明了這一點。

實驗大致內容:使用toXML()方法輸出XML文件,使用ObjectInputStream解析,發現會在讀取的時候拋出CannotResolveClassException異常。

錯誤信息:

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

a : a

       因此我認為在解析文件的時候必須先要確定這個文件是由什么方式生成的,然后在解析它,對于使用Dom,Dom4j,XPP等不同方式解析尚未嘗試。以上測試是在默認的基礎上實驗的,默認為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()生成出來的。對于Reader沒有太多的要求。

 

 

XStreamJava.BeanXML工具的比較

 

       XStream主要作用是將序列化的對象轉化為一個XML文件或將XML文件解析為一個對象。當然并非只有它可以做到,很多其它工具一樣可以,在Java中存在這樣兩個類XMLDecoderXMLEncoder,它們是在Java.Bean包下的,它們的作用是將JavaBean轉化為XML或將XML文件轉化為一個Java Bean。

       XMLDecoder是通過一個輸入流將對象從輸入流中取出并轉化為一個實例的方法。它所需要的就是一個輸入流及一個轉化過程。

 

       XMLDecoder的實例:

 

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

       ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileStr));//創建一個ObjectInputStream用于輸入。

       XMLDecoder xmld=new XMLDecoder(in);//創建一個XMLDecoder對象。

       延用前面所使用PrintUnit這個Bean。

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

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

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

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

 

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

 

       XMLEncoder的實例:

 

       String fileStr=”xstream.xml”;//定義一個輸入的目標文件。

       ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileStr));//創建一個對象輸出流。

       XMLEncoder xmle=new XMLEncoder(out);//創建一個XMLEncoder對象。

       延用前面所使用PrintUnit這個Bean

//創建并初始化PrintUnit對象。

PrintUnit pu=new PrintUnit();

pu.setA(“AAA”);

pu.setB(“BBB”);

pu.setC(“CCC”);

 

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

       xmle.flush();//刷新

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

 

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

      

       XML文件的內容:

 

?_ <?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>

 

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

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

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

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