XStream
使用XStream的初衷
研究和使用XStream的原因是我在項目中的一個預研。在項目中需要應用到對XML文件的管理和配置,因此需要一個能夠?qū)ο蟊4鏋?/SPAN>XML的工具庫,在這里有多種方法實現(xiàn),我也研究并進行了比對,比如與Zeus工具的比對,與Java自身的XML工具庫的比對等。在這里,我就描述下我的XStream學習過程和研究結(jié)果。
XStream簡單介紹
XStream是一個開源項目,一套簡單實用的類庫,用于序列化對象與XML對象之間的相互轉(zhuǎn)換。將XML文件內(nèi)容解析為一個對象或?qū)⒁粋€對象序列化為XML文件。
XStream可以用于JDK1.3以上的版本使用,我是在JDK1.5下使用它的。
XStream的相關(guān)信息可以到http://xstream.codehaus.org/下查看,它有專門的JavaDoc,可以方便的閱讀Xstream的函數(shù)及方法。
XStream中主要的類為XStream,它用于序列化對象與XML 對象之間的相互轉(zhuǎn)換。簡單的使用它就可以解決很多問題。
XStream中主要的方法也是我用的比較多的是fromXML()和toXML()。
fromXML用于從XML中將對象解析出來。
toXML用于將對象序列化為XML文件。
在XStream中我還使用HierarchicalStreamWriter,HierarchicalStreamReader,createObjectInputStream(),createObjectOutputStream(),主要是用于對象的輸入輸出。
下面我們來研究下XStream的工作方式。
XStream的實例——將一個序列化對象轉(zhuǎn)化為XML對象。
一,創(chuàng)建XStream對象。
XStream xstream=new XStream();
用默認構(gòu)造器構(gòu)造了一個名為xstream的XStream的對象。默認構(gòu)造器所使用XML解析庫為Xpp3庫,XPP3是一種運行效率非常高的XML全解析實現(xiàn)。
二,創(chuàng)建需要序列化的對象。
比如這個類就叫PrintUnit。
構(gòu)造也比較簡單,一個簡單的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。
創(chuàng)建并初始化PrintUnit。
PrintUnit pu=new PrintUnit();
pu.setA("A11");
pu.setB("B22");
pu.setC("C33");
三,創(chuàng)建Writer。
創(chuàng)建一個輸出流,至于怎么輸出我發(fā)現(xiàn)可以使用多種方法,其實原理是一樣的。
在這里就不得不提到HierarchicalStreamWriter,HierarchicalStreamWriter是一個接口,從字面上意思來說它是有等級的輸入流。同樣在XStream中也有不少這個接口的實現(xiàn)類用于輸出。我現(xiàn)在所用過的有CompactWriter和PrettyPrintWriter這2個。
我是這樣做的:
String str="stream.xml"; //本目錄下的一個名為stream的XML文件
PrintWriter pw=new PrintWriter(str);//創(chuàng)建一個PrintWriter對象,用于輸出。
之后選用一個HierarchicalStreamWriter的實現(xiàn)類來創(chuàng)建輸出。
選用CompactWriter創(chuàng)建:
CompactWriter cw=new CompactWriter(pw);
選用PrettyPrintWriter創(chuàng)建:
PrettyPrintWriter ppw=new PrettyPrintWriter(pw);
兩者所使用的方法都是很簡單的。
CompactWriter與PrettyPrintWriter的區(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)的一些其它方式。
先說下我所使用的方式它們各自的不同點,從工作原理上說它們是相似的,但是做法各不相同。
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輸出到某個地方存儲。
我所使用的是第二種方式,使用前面已經(jīng)做好的Pw就可以實現(xiàn)輸出,它其實很簡單不需要再去做其它定義,只需要一個PrintWriter對象和需要序列化的Object即可。
直接調(diào)用xstream.toXML(printUnit,pw);就能輸出XML文件,在這里是輸出到該目錄下的stream.xml中。這里的輸出都是覆蓋性的,不是末尾添加形式。
使用ObjectOutputStream方式,簡單說它就是生成一個對象輸出流。
ObjectOutputStream obj_out = xstream.createObjectOutputStream(ppw);
使用XStream的createObjectOutputStream方法創(chuàng)建一個ObjectOutputStream對象,用于XML的輸出。這里使用的是PrettyPrintWriter的方式。 之后調(diào)用writerObject方法既可,使用方法與其它輸出流類似。
obj_out.writeObject(pu);
obj_out.close();
使用marshal方式,其實marshal方法和toXML方法是相同的。在調(diào)用toXML方法進行輸出時,在XStream內(nèi)部是需要調(diào)用marshal方法的,然后它再去調(diào)用對象marshallingStrategy的marshal方法。所以做toXML其實和marshal是相同的,在這里只是想更加說明它的工作方式。
使用 void marshal(java.lang.Object obj, HierarchicalStreamWriter writer)方法。
延續(xù)上面的例子,在這里可以這樣寫:xstream.marshal(pu,ppw);
需要注意的是,和toXML不同的是參數(shù),一個是PrintWriter對象一個則是PrettyPrintWriter對象。因為marshal中需要
HierarchicalStreamWriter,而PrettyPrintWriter則是實現(xiàn)了HierarchicalStreamWriter接口的實現(xiàn)類。
結(jié)果和toXML是相同的。
五,結(jié)果:
<object-stream>
<PrintUnit>
<a>A11</a>
<b>B22</b>
<c>C33</c>
</PrintUnit>
</object-stream>
經(jīng)過以上5步的操作既可將一個序列化對象轉(zhuǎn)化為XML對象。
toXML內(nèi)部調(diào)用圖:
toXML操作時的內(nèi)部調(diào)用圖,自己隨意畫的。有些沒有詳細說明。
XStream的實例——將XML文件轉(zhuǎn)化為一個對象
通過上面的一個例子不難看出XStream簡便性,既然有了輸出就一定會有輸入。
輸入方我們將會使用ObjectInputStream。
與輸出相同我們需要有一個XStream對象,暫且名為xstream。之后需要讀取的XML文件地址目錄信息。沿用上面的例子。
String inputStr="xstream.xml";
XStream xstream=new XStream();
我們需要通過對象流進行輸入操作,所以需要FileReader和BufferedReader。
FileReader fr=new FileReader(inputStr);
BufferedReader br=new BufferedReader(fr);
創(chuàng)建對象輸入流
ObjectInputStream obj_input=xstream.createObjectInputStream(br);
創(chuàng)建對象,還是使用PrintUnit這個對象。
PrintUnit pu2;
通過ObjectInputStream中的readObject()方法將對象從XML文件中讀取出來。
pu2=(PrintUnit)obj_input.readObject();
獲取值:
System.out.println(pu2.getB());
控制臺:
B22
從整個輸入的過程來看,是一個文件的讀取,將其中的對象數(shù)據(jù)取出來,然后再對這個對象數(shù)據(jù)進行操作。內(nèi)容也比較簡單通過ObjectInputStream輸入對象。
通過以上的輸入輸出例子,我想大家應該很容易就能理解XStream是如何實現(xiàn)的。
FomXML
上面使用的是以ObjectInputStream的方式進行XML與對象之間進行轉(zhuǎn)換的。下面我將使用XStream中的fromXML()方法進行轉(zhuǎn)換。
首先在使用fromXML我發(fā)現(xiàn)一個問題,它必須使用正確的解析方式或輸出方式對應的輸入方式才可以正常解析讀取文件,這個問題有點怪,不過確實存在,當我使用前面ObjectOutputStream方式輸出的XML文件,用fromXML()解析讀取時,它會報錯。
錯誤信息:
Exception in thread "main" com.thoughtworks.xstream.alias.CannotResolveClassException: object$stream : object$stream
信息內(nèi)容為:不能解析這個文件。我認為它和輸出方式有關(guān),因為上面例子中使用的是ObjectOutputStream,當我反過來做了一個實驗后也證明了這一點。
實驗大致內(nèi)容:使用toXML()方法輸出XML文件,使用ObjectInputStream解析,發(fā)現(xiàn)會在讀取的時候拋出CannotResolveClassException異常。
錯誤信息:
Exception in thread "main" com.thoughtworks.xstream.alias.CannotResolveClassException:
a : a
因此我認為在解析文件的時候必須先要確定這個文件是由什么方式生成的,然后在解析它,對于使用Dom,Dom4j,XPP等不同方式解析尚未嘗試。以上測試是在默認的基礎(chǔ)上實驗的,默認為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沒有太多的要求。
XStream與Java.Bean中XML工具的比較
XStream主要作用是將序列化的對象轉(zhuǎn)化為一個XML文件或?qū)?/SPAN>XML文件解析為一個對象。當然并非只有它可以做到,很多其它工具一樣可以,在Java中存在這樣兩個類XMLDecoder和XMLEncoder,它們是在Java.Bean包下的,它們的作用是將JavaBean轉(zhuǎn)化為XML或?qū)?/SPAN>XML文件轉(zhuǎn)化為一個Java Bean。
XMLDecoder是通過一個輸入流將對象從輸入流中取出并轉(zhuǎn)化為一個實例的方法。它所需要的就是一個輸入流及一個轉(zhuǎn)化過程。
XMLDecoder的實例:
String fileStr=”xstream.xml”;//XML文件,在本目錄下,延用上次使用文件。
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileStr));//創(chuàng)建一個ObjectInputStream用于輸入。
XMLDecoder xmld=new XMLDecoder(in);//創(chuàng)建一個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則是可以自定義的,它可以使用多中方式進行解析。這些是我個人所發(fā)現(xiàn)的一些不同點。
XMLEncoder是通過一個輸出流將對象序列化并輸出為XML文件。它所需要的是一個輸出流及一個輸出方式。
XMLEncoder的實例:
String fileStr=”xstream.xml”;//定義一個輸入的目標文件。
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileStr));//創(chuàng)建一個對象輸出流。
XMLEncoder xmle=new XMLEncoder(out);//創(chuàng)建一個XMLEncoder對象。
延用前面所使用PrintUnit這個Bean。
//創(chuàng)建并初始化PrintUnit對象。
PrintUnit pu=new PrintUnit();
pu.setA(“AAA”);
pu.setB(“BBB”);
pu.setC(“CCC”);
xmle.writeObject(pu);//使用XMLEncode的writeObject方法輸出pu
xmle.flush();//刷新
xmle.close();//關(guān)閉輸出流
從上面的代碼不難看出,使用XMLEncode方式將對象序列化并輸出也是很方便的,簡單調(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>
不知道是我哪里沒有處理,還是實際并不是像我想象的哪么簡單,使用XMLEncoder所輸出的XML文件中有一定的問題,雖然它很詳細,比起XStream所生成的更多,包括了XML和Java的版本看上去更像是個完整的XML文件,不過再細看它們兩生成的XML格式內(nèi)容,完全不同,這個我想就是它們最大的區(qū)別。這讓我想到了很多內(nèi)容:工作方式,解析器,轉(zhuǎn)換方式等。大家有沒發(fā)現(xiàn)在開始和結(jié)束都存在一些亂碼數(shù)據(jù),難道在XMLEncoder輸出過程中或數(shù)據(jù)轉(zhuǎn)換中內(nèi)容已經(jīng)存在“臟”數(shù)據(jù)了?還是我所使用的輸出方式存在問題?哎…一個又一個問題出現(xiàn)了。我想我需要再進一步的研究和學習才能得到答案。
不過盡管有這個那個的問題,使用Java本身自帶的XML工具還是一樣很實用的,讀取和輸出一樣可用,操作也很靈活。因此我覺得在某些場合使用特定的工具可能會更好,利用XMLEncoder和XMLDecoder同樣可以解決一些問題。
我的這個使用XMLDecoder和XMLEncoder的序列化格式輸出暫研究到這里。
楓情·太子爺
2005年12月16日