目錄
項目簡介... 4
典型的應用場合... 5
系統架構分析... 5
實例講解... 6
創建需要序列化的對象類... 6
初始化XStream類... 9
注冊需要轉換的類的別名... 10
將對象序列化為XML文檔... 10
從XML文件反序列化成對象... 11
結束語... 11
參考資料... 11
開源項目XStream是一套簡單實用的類庫,用于序列化對象與XML對象之間的相互轉換。本文以XStream 1.1.2版進行說明,它具有以下幾個特點:
? 靈活易用:在更高的層次上提供了簡單、靈活、易用的統一接口,用戶無需了解項目的底層細節
? 無需映射:大多數對象都可以在無需映射的情況下進行序列化與反序列化的操作
? 高速穩定:設計時力求達到的最重要的指標是解析速度快、占用內存少,以使之能夠適用于大的對象處理或是對信息吞吐量要求高的系統
? 清晰易懂:項目采用reflection機制得到無冗余信息的XML文件。所生成的XML文件較本地Java序列化產物更簡潔,格式更清晰,更便于用戶閱讀
? 無需修改:完全序列化包括private和final類型在內的全部內部字段。支持非公有類和內部類,類可以沒有缺省的構造函數
? 易于集成:通過實現特定的接口,XStream可以直接與其它任何樹型結構進行序列化與反序列化操作(而不僅僅是XML格式)
? 靈活轉換:轉換策略是可以定制的,允許用戶自定義特殊類型的對象如何以XML格式存儲。
? 錯誤處理:由于XML資料不合法而造成異常時,會提供詳細地診斷信息幫助處理問題。
XStream主要應用于以下場合:
ü 數據對象的持久化
ü 數據交換
ü 配置文件
XStream的架構主要由四部分組成:
2 Converters(轉換器)
當XStream遇到需要輪換的對象時,它會委派給合適的轉換器實現,XStream為通用類型提供了多種轉換器實現,包括基本數據類型、String、Collections、Arrays、null、Date,等等。
XStream提供了缺省的轉換器,當需要轉換的數據對象沒有匹配的轉換器時會使用。是通過反射機制自動完成對對象內所有字段的映射。
2 IO(輸入/輸出)
XStream是通過接口HierarchicalStramWriter和HierarchialStreamReader從底層XML數據中抽象而來的,上面的接口分別用于序列化和反序列化操作。
該特性使得XStream可以直接使用XML解析類從數據流中讀取數據,或者是直接從已經存在的結構中提取數據(比如DOM)。如果XStream所操作的XML數據已經部分被其它XML解析類處理過了(比如SOAP類的實例),這樣就可以避免在我們這一層的再次解析操作。
2 Context(上下文引用)
在XStream序列化或反序列化對象時,它會創建兩個類MarshallingContext和UnmarshallingContext,由它們來處理數據,以及委派合適的轉換器。
XStream提供了三對上下文的缺省實現,它們之間有著細微的差別。缺省值可以通過方法XStream.setMode()來改變,需要傳遞下面參數中的一個:
? XStream.XPATH_REFERENCES
(缺省的)通過XPath引用來標識重復的引用。這樣產生的XML具有最小的混亂性。
? XStream.ID_REFERENCES
使用ID引用來標識重復的引用。在一些場合,比如使用手寫XML時,這樣將會更易于操作
? XStream.NO_REFERENCES
這種情況將失去對圖形對象的支持,僅把對象看作為樹型結構。重復的引用被視作兩個不同的對象,循環引用會導致異常產生。相對于上面兩種模式,這種模式速度會更快,占用內存會更少。
2 Facade(統一入口)
主要類XStream用作所有項目的入口點。它將上面所提及的重要組件集成在一起,提供更簡單易用的API操作。
下面我們通過一個簡單的例子來了解XStream是如何工作的。
/**個人信息類*/
public class Person
{
/**
* 構造函數
* @param fn 名稱前部分
* @param ln名稱后部分
* @param faxn 傳真號碼
* @param mobilen 移動電話
*/
public Person(String fn, String ln, PhoneNumber faxn, PhoneNumber mobilen)
{
this.firstName = fn;
this.lastName = ln;
this.faxNumber = faxn;
this.mobileNumber = mobilen;
}
/**
* 構造函數
* @param fn 名稱前部分
* @param ln名稱后部分
*/
public Person(String fn, String ln)
{
this.firstName = fn;
this.lastName = ln;
}
private String firstName;
private String lastName;
private PhoneNumber faxNumber;
private PhoneNumber mobileNumber;
/**
* @return Returns the faxNumber.
*/
public PhoneNumber getFaxNumber()
{
return faxNumber;
}
/**
* @param faxNumber
* The faxNumber to set.
*/
public void setFaxNumber(PhoneNumber faxNumber)
{
this.faxNumber = faxNumber;
}
/**
* @return Returns the firstName.
*/
public String getFirstName()
{
return firstName;
}
/**
* @param firstName
* The firstName to set.
*/
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
/**
* @return Returns the lastName.
*/
public String getLastName()
{
return lastName;
}
/**
* @param lastName
* The lastName to set.
*/
public void setLastName(String lastName)
{
this.lastName = lastName;
}
/**
* @return Returns the mobileNumber.
*/
public PhoneNumber getMobileNumber()
{
return mobileNumber;
}
/**
* @param mobileNumber
* The mobileNumber to set.
*/
public void setMobileNumber(PhoneNumber mobileNumber)
{
this.mobileNumber = mobileNumber;
}
}
/**電話號碼信息類*/
public class PhoneNumber
{
private int phoneId;
private String phoneNumber;
/**
* 構造函數
* @param phoneId ID號碼
* @param phoneNumber 電話號碼
*/
public PhoneNumber(int phoneId, String phoneNumber)
{
super();
this.phoneId = phoneId;
this.phoneNumber = phoneNumber;
}
/**
* @return Returns the phoneNumber.
*/
public String getPhoneNumber()
{
return phoneNumber;
}
/**
* @param phoneNumber
* The phoneNumber to set.
*/
public void setPhoneNumber(String phoneNumber)
{
this.phoneNumber = phoneNumber;
}
/**
* @return Returns the phoneId.
*/
public int getPhoneId()
{
return phoneId;
}
/**
* @param phoneId
* The phoneId to set.
*/
public void setPhoneId(int phoneId)
{
this.phoneId = phoneId;
}
}
這里定義的屬性都為private,XStream沒有強制規定屬性的可見性,默認情況下所有屬性都會進行轉換;雖然XStream不強制要求你必須要有setter和getter方法,也不要求你要有一個默認的類構造方法,但是在實際應用時,這些還是必須的,因為你在程序中需要為對象設置屬性,需要構造對象,如果沒有這些方法程序是不能編譯通過的。
使用以下的語句進行初始化操作:
XStream xstream = new XStream();
默認情況下,XStream會采用Xpp3庫,XPP3是一種運行效率非常高的XML全解析實現。如果你不想依靠Xpp3庫的話,也可以使用一個標準的JAXP DOM解析器,可以采用以下語句進行初始化:
//不使用XPP3庫
XStream xstream = new XStream(new DomDriver());
此xstream實例,為線程安全的,可以供多個線程進行調用,共享使用。參考com.thoughtworks.xstream.io.xml包,會發現系統提供了多種標識解析器供我們選擇,包括,DomDriver、JDomDriver、StaxDriver等等。
現在,為了使XStream輸出的XML文件更簡練,我們要為需要轉換的用戶類設置別名,這些明會在轉換過程中用于結點設置,注冊別名使用以下的語句:
xstream.alias(“person”, Person.class);
xstream.alias(“phonenumber”, PhoneNumber.class);
當然,這一步不是必須的,如果不進行注冊的話,XStream默認會在轉換時,將用戶類的全限定名稱加入到XML文件中,如com.test.Person,如果在數據量較大的時候,生成的XML文件會增大不少。
到了這一步,我們就可以將一個Java對象序列化為XML文檔了,先聲明一個Person對象,然后為該對象設置一個手機號碼,一個傳真號碼,使用下面的語句:
//生成Person對象,并注冊屬性
Person joe = new Person("Bill", "Gates");
joe.setFaxNumber(new PhoneNumber(101, "83501194"));
joe.setMobileNumber(new PhoneNumber(102, "13686447788"));
下面就是將生成的對象序列化,我們需要做的只是簡單的使用下面的一個語句就可以:
String xmls = xstream.toXML(joe);
從結果我們可以看出,生成的XML文件非常簡潔,除了必要的結點外,沒有一絲多余的信息存在。參考下面的生成結果:
<person>
<firstName>Bill</firstName>
<lastName>Gates</lastName>
<faxNumber>
<phoneId>101</phoneId>
<phoneNumber>83501194</phoneNumber>
</faxNumber>
<mobileNumber>
<phoneId>102</phoneId>
<phoneNumber>13686447788</phoneNumber>
</mobileNumber>
</person>
從XML文件反序列化成對象
從一個XML文件反序列化出一個對象同樣簡單,一起來看下面的代碼:
Person newJoe = (Person)xstream.fromXML(xmls);
通過上面的實例,我們可以看出,使用XStream來處理對象的序列化和反序列化很簡單,只需要幾行代碼即可,而且該項目對標識的XML解析器有很好的支持,最重要的是生成的XML很“干凈”,沒有過多的冗余信息,該項目還在持續進行中,讓我們一起來關注它吧!
l XStream官方資料 http://xstream.codehaus.org/index.html
l 本例相關代碼(附件)