JAVA bean與XML互轉的利器---XStream
阿新 • • 發佈:2017-12-28
pub 普通 ati mat his cit true 是我 package
有沒有覺得很奇怪為什麽會有“xstreamTest.Person”的標簽?對照下上面提到的JAVA bean這個標簽是來自於JAVA bean的類全路徑的。
可是這個並不是我想要的啊,有沒辦法改變?有,簡單嗎? 簡單!
2. 起別名
家丁我們希望將“xstreamTest.Person” 這個莫名其妙的element標簽改為“person”我們應該這麽做。
最近在項目中遇到了JAVA bean 和XML互轉的需求, 本來準備循規蹈矩使用dom4j忽然想起來之前曾接觸過的XStream, 一番研究豁然開朗,利器啊利器, 下來就XStream的一些用法與大家分享。
XStream是大名鼎鼎的thought works下的一個開源項目, 主要功能是提供JAVA bean 和XML文本之間的轉換,另外還提供JAVA bean和JSON之間的轉換,這個不在本次討論的範圍內。
XStream進行轉換是非常簡單的,對JAVA bean沒有任何要求:
- 不要求對private屬性提供access方法(set/get)。
- 不要求提供默認構造函數。
- 基本轉換
- 對象起別名
- 處理屬性
- 處理List
- 忽略field
package xstreamTest; public class Person { private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
轉換代碼是這樣的:
XStream xstream = new XStream(); Person person = new Person(); person.setName("pli"); person.setAge(18); System.out.println(xstream.toXML(person));
我們得到了這樣的結果:
<xstreamTest.Person> <name>pli</name> <age>18</age> </xstreamTest.Person>
package xstreamTest; @XStreamAlias("person") public class Person { private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
而執行代碼會變成這樣:
XStream xstream = new XStream(); xstream.autodetectAnnotations(true); Person person = new Person(); person.setName("pli"); person.setAge(18); System.out.println(xstream.toXML(person));
這樣我們就得到了想要的:
<person> <name>pli</name> <age>18</age> </person>這裏要提到的是“xstream.autodetectAnnotations(true);” 這句代碼告訴XStream去解析JAVA bean中的annotation。這句代碼有一個隱患,會在後面討論。 別名可以改變任何你想在序列化時改變的對象名字,類,屬性甚至包名,所用到的其實就是“XSstreamAlias”這個annotation。 3. 處理屬性 如果想要將JAVA bean中的“age”屬性作為XML中person標簽的一個attribute該怎麽辦呢。 這裏介紹另外一個annotation:@XStreamAsAttribute, 我們的JAVA bean變成了這樣:
@XStreamAlias("person") public class Person { private String name; @XStreamAsAttribute private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
結果是這樣的:
<person age="18"> <name>pli</name> </person>好玩吧。 4. 處理List 如果JAVA bean中有List是什麽情形呢。
@XStreamAlias("person") public class Person { private String name; @XStreamAsAttribute private int age; List<String> girlFriends; public List<String> getGirlFriends() { return girlFriends; } public void setGirlFriends(List<String> girlFriends) { this.girlFriends = girlFriends; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
直接轉換我們會得到這樣的結果:
<person age="18"> <name>pli</name> <girlFriends> <string>YuanYuanGao</string> <string>QiShu</string> <string>BoZhiZhang</string> </girlFriends> </person>
結果其實也不賴,XStream在這裏提供了一個@XStreamImplicit(itemFieldName=***)的annotation來滿足用戶想將List的根節點去掉和改變列表名字的需求,對應到我們的例子上就是去掉<girlFriends>標簽和改變"<string>".我們來看看效果。
@XStreamAlias("person") public class Person { private String name; @XStreamAsAttribute private int age; @XStreamImplicit(itemFieldName="girl") List<String> girlFriends; public List<String> getGirlFriends() { return girlFriends; } public void setGirlFriends(List<String> girlFriends) { this.girlFriends = girlFriends; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
結果是這樣:
<person age="18"> <name>pli</name> <girl>YuanYuanGao</girl> <girl>QiShu</girl> <girl>BoZhiZhang</girl> </person>
5. 忽略屬性 如果在JAVA bean中有些屬性不想被序列化,XStream提供了解決這個需求的annotation: @XStreamOmitField 比如說不想講girlfriends這個List序列化
@XStreamAlias("person") public class Person { private String name; @XStreamAsAttribute private int age; @XStreamImplicit(itemFieldName="girl") @XStreamOmitField List<String> girlFriends; public List<String> getGirlFriends() { return girlFriends; } public void setGirlFriends(List<String> girlFriends) { this.girlFriends = girlFriends; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
結果是這樣:
<person age="18"> <name>pli</name> </person>6. Converter Converter這個是屬於XStream中的高級特性了,用於基本功能不能滿足的情況下讓客戶自己定制序列化/反系列化的細節,我們還是通過一個例子進行說明。 假如我要往JAVA bean中添加一個類型為Date的屬性:
@XStreamAlias("person") public class Person { private String name; @XStreamAsAttribute private int age; @XStreamImplicit(itemFieldName="girl") @XStreamOmitField List<String> girlFriends; Date birthday; public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public List<String> getGirlFriends() { return girlFriends; } public void setGirlFriends(List<String> girlFriends) { this.girlFriends = girlFriends; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
看看直接序列化的結果:
<person age="18"> <name>pli</name> <birthday>2012-08-04 04:35:01.857 UTC</birthday> </person>
還不錯,但是生日只需要年月日就行了,沒必要精確到毫秒,這怎麽辦呢,只能使用converter,我們這是就需要寫代碼了。
public class DateConverter implements Converter { @Override public boolean canConvert(Class clazz) { return (Date.class).equals(clazz); } @Override public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { Date date = (Date) object; Calendar calendar = Calendar.getInstance(); calendar.setTime(date); SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd"); writer.setValue(format.format(calendar.getTime())); } @Override public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) { return null; } }稍微解釋下這段代碼:DateConverter 實現了借口Converter,實現了接口中的三個方法:
- public boolean canConvert(Class clazz) 用來檢測本converter是否能夠轉換輸入的類型。
- public void marshal(Object object, HierarchicalStreamWriter writer,MarshallingContext context) 序列化的方法(JAVA bean --> XML)
- public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) 反序列化的方法。因為本例用不到所以沒有實現。
@XStreamAlias("person") public class Person { private String name; @XStreamAsAttribute private int age; @XStreamImplicit(itemFieldName="girl") @XStreamOmitField List<String> girlFriends; @XStreamConverter(value=DateConverter.class) Date birthday; public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public List<String> getGirlFriends() { return girlFriends; } public void setGirlFriends(List<String> girlFriends) { this.girlFriends = girlFriends; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
看看結果:
<person age="18"> <name>pli</name> <birthday>2012-50-04</birthday> </person>另外在這裏簡單說說converter的原理: 其實XStream轉換過程就是執行一個個converter的過程,只不過使用的大部分converter都是內建好的,XStream遇到一個待轉換的object首先去查找能夠轉換這個object的轉換器(converter)怎麽找呢,就是通過converter的canConvert(Class clazz)這個方法,返回為true就是可以轉換。明白了吧。 XStream的限制: Xstream已經是很不錯的東西了,如果真要找不足,我發現有兩點。 1. 反序列化的時候無法使用autodetectAnnotations()方法通知XStream對象去識別annotation。 還記的前面代碼中xstream.autodetectAnnotations(true); 嗎, 這句代碼的意思是告訴XStream對象需要自動識別annotation, 這在序列化(JAVA bean-->XML)的時候沒什麽問題。但是在反序列化的時候就有問題了,原因官網上說的比較模糊,總之就是不行,只能通過xstream.processAnnotations(Class clazz) 來顯式的註冊需要使用annotation的類才行,如果JAVA bean很多就會比較麻煩。但一般來說JAVA bean在代碼組織結構中都比較集中,如放在聽一個package下,這樣也好辦,可以再程序中將該package下的JAVA bean都獲取,然後使用xstream.processAnnotations(Class[] clazzs) 批量註冊。 2. Null 屬性無法被序列化。 之前舉的例子JAVA bean中的屬性都是被初始化以後才進行序列化的,如果沒有初始化就進行序列化會怎樣呢 ,還是舉個例子
@XStreamAlias("person") public class Person { private String name = "pli"; @XStreamAsAttribute private int age = 19; @XStreamImplicit(itemFieldName="girl") @XStreamOmitField List<String> girlFriends; @XStreamConverter(value=DateConverter.class) Date birthday = new Date(); public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public List<String> getGirlFriends() { return girlFriends; } public void setGirlFriends(List<String> girlFriends) { this.girlFriends = girlFriends; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
我想將其它屬性都進行了初始化但是沒有將girlFriends這個屬性初始化,即使說girlFriends==null. 序列化以後會怎樣呢?
<person age="18"> <name>pli</name> <birthday>2012-36-04</birthday> </person>
girlFriends這個屬性壓根就沒有被序列化,其實我是想讓它序列化成這個樣子:
<person age="18"> <name>pli</name> <birthday>2012-36-04</birthday> <girlFriends/> </person>
原文:http://blog.csdn.net/pushme_pli/article/details/7829621
JAVA bean與XML互轉的利器---XStream