——Castor可以完成Java和XML的相互轉換
前面有介紹過json-lib這個框架,在線博文:[http://www.cnblogs.com/hoojo/archive/2011/04/21/2023805.html](http://www.cnblogs.com/hoojo/archive/2011/04/21/2023805.html)
以及Jackson這個框架,在線博文:[http://www.cnblogs.com/hoojo/archive/2011/04/22/2024628.html](http://www.cnblogs.com/hoojo/archive/2011/04/22/2024628.html)
它們都可以完成Java對象到XML的轉換,但是還不是那么的完善。還有XStream對JSON及XML的支持,它可以對JSON或XML的完美轉換。在線博文:
[http://www.cnblogs.com/hoojo/archive/2011/04/22/2025197.html](http://www.cnblogs.com/hoojo/archive/2011/04/22/2025197.html)
這里將介紹Castor來完成Java對象到xml的相互轉換。它是怎么樣轉換的?和前面不同的是castor可以用一個mapping.xml文件來描述轉換后的Java對象的xml基本形態,類似于xStream的annotation,這點還是非常不錯的。下面我們就來看看Castor是怎么樣完成Java對象到XML之間的相互轉換吧。
### 一、準備工作
1、 官方資源
本示例會運用到如下依賴包(jar包):
[](http://hi.csdn.net/attachment/201104/25/0_13036993615eBC.gif)
資源及jar包下載:[http://www.castor.org/download.html](http://www.castor.org/download.html)
junit jar下載地址:
[https://github.com/KentBeck/junit/downloads](https://github.com/KentBeck/junit/downloads)
關于官方提供的mapping配置相關示例、文檔:
[http://www.castor.org/xml-mapping.html](http://www.castor.org/xml-mapping.html)
ibm提供的castor方面的文檔資料:
[http://www.google.com.hk/search?hl=zh-CN&newwindow=1&safe=strict&client=aff-cs-360se&hs=Gon&biw=1349&bih=603&q=castor+site%3Awww.ibm.com%2Fdeveloperworks%2Fcn%2Fxml%2F&aq=f&aqi=&aql=&oq](http://www.google.com.hk/search?hl=zh-CN&newwindow=1&safe=strict&client=aff-cs-360se&hs=Gon&biw=1349&bih=603&q=castor+site%3Awww.ibm.com%2Fdeveloperworks%2Fcn%2Fxml%2F&aq=f&aqi=&aql=&oq)=
2、 程序測試運行代碼
~~~
package com.hoo.test;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.hoo.entity.Account;
import com.hoo.entity.AccountArray;
import com.hoo.entity.Birthday;
import com.hoo.entity.ListBean;
import com.hoo.entity.MapBean;
/**
* function:Castor完成Java對象到XML的相互轉換
* 依賴jar: castor-1.3.jar
* castor-1.3-core.jar
* junit-4.8.2.jar
* log4j-1.2.16.jar
* commons-logging.jar
* @author hoojo
* @createDate 2011-4-21 下午07:57:26
* @file CastorTest.java
* @package com.hoo.test
* @project WebHttpUtils
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class CastorTest {
private Account bean = null;
private Mapping mapping = new Mapping();
private StringWriter writer = null;
private StringReader reader = null;
@Before
public void init() {
bean = new Account();
bean.setAddress("北京");
bean.setEmail("email");
bean.setId(1);
bean.setName("jack");
Birthday day = new Birthday();
day.setBirthday("2010-11-22");
bean.setBirthday(day);
try {
/**
* 加載mapping.xml,此文件是對需要轉換的Java對象的配置描述,
* 即:轉換后的Java對象的xml內容的轉換規則
*/
mapping.loadMapping(System.getProperty("user.dir") + "//src//mapping.xml");
} catch (IOException e) {
e.printStackTrace();
} catch (MappingException e) {
e.printStackTrace();
}
}
@After
public void destory() {
bean = null;
mapping = null;
try {
if (writer != null) {
writer.flush();
writer.close();
}
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
System.gc();
}
public void fail(Object o) {
System.out.println(o);
}
public void failRed(Object o) {
System.err.println(o);
}
}
~~~
Mapping對象可以完成Java對象到XML的編組和解組,它需要先設定一個mapping.xml,通過xml對JavaObject的描述。來完成JavaObject的編組、解組工作。
3、 看看即將被轉換的JavaEntity代碼
Account
~~~
package com.hoo.entity;
public class Account {
private int id;
private String name;
private String email;
private String address;
private Birthday birthday;
//setter、getter
@Override
public String toString() {
return this.id + "#" + this.name + "#" + this.email + "#" + this.address + "#" + this.birthday;
}
}
~~~
Birthday
~~~
package com.hoo.entity;
public class Birthday {
private String birthday;
public Birthday(String birthday) {
super();
this.birthday = birthday;
}
//getter、setter
public Birthday() {}
@Override
public String toString() {
return this.birthday;
}
}
~~~
AccountArray
~~~
package com.hoo.entity;
public class AccountArray {
private Account[] accounts;
private int size;
public int getSize() {
size = accounts.length;
return size;
}
public void setSize(int size) {
this.size = size;
}
public Account[] getAccounts() {
return accounts;
}
public void setAccounts(Account[] accounts) {
this.accounts = accounts;
}
}
~~~
ListBean
~~~
package com.hoo.entity;
import java.util.List;
public class ListBean {
private String name;
private List list;
//setter、getter
}
~~~
MapBean
~~~
package com.hoo.entity;
import java.util.Map;
public class MapBean {
private Map map;
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
}
~~~
### 二、編組JavaObject到XML
1、 將JavaBean編組,轉換成XML
~~~
/**
* function:將Javabean編組,轉換成XML
* @author hoojo
* @createDate 2011-4-22 下午12:08:48
*/
@Test
public void writeBean2XML() {
writer = new StringWriter();
try {
//編組
Marshaller.marshal(bean, writer);
fail(writer);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
代碼很簡單,通過Marshaller的marshal方法來完成Java對象到XML的編組(序列化、轉換)工作。
運行后的結果如下:
~~~
"1.0" encoding="UTF-8"?>
"1">
北京
emailjack
2010-11-22
~~~
2、 將List集合轉換成XML
~~~
/**
* function:將List轉換成xml
* @author hoojo
* @createDate 2011-4-22 下午12:11:00
*/
@Test
public void writeList2XML() {
writer = new StringWriter();
List list = new ArrayList();
list.add(bean);
bean = new Account();
bean.setName("tom");
bean.setId(223);
list.add(bean);
try {
Marshaller.marshal(list, writer);
fail(writer);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
運行后,結果如下:
~~~
"1.0" encoding="UTF-8"?>
"http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" id="1" xsi:type="java:com.hoo.entity.Account">
北京
emailjack2010-11-22
"http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" id="223" xsi:type="java:com.hoo.entity.Account">
tom
~~~
怎么樣,List存放的是2個Account吧。
3、 將Array數組轉換成XML
~~~
/**
* function:將Array數組轉換成XML
* @author hoojo
* @createDate 2011-4-22 下午12:11:25
*/
@Test
public void writeArray2XML() {
writer = new StringWriter();
Account[] acc = new Account[2];
acc[0] = bean;
bean = new Account();
bean.setName("tom");
bean.setId(223);
acc[1] = bean;
try {
Marshaller.marshal(acc, writer);
fail(writer);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
結果如下:
~~~
"1.0" encoding="UTF-8"?>
"1">
北京
emailjack
2010-11-22
"223">tom
~~~
4、 轉換其他Java類型
~~~
/**
* function:將Java常用類型編組成xml
* @author hoojo
* @createDate 2011-4-22 下午12:11:44
*/
@Test
public void writeObject2XML() {
writer = new StringWriter();
try {
Marshaller.marshal(true, writer);
Marshaller.marshal(9527, writer);
Marshaller.marshal(2.2f, writer);
Marshaller.marshal(1.11d, writer);
Marshaller.marshal("lucy", writer);
Marshaller.marshal("hello castor".getBytes(), writer);
Marshaller.marshal(new char[] { 'a', 'b', 'c' }, writer);
Marshaller.marshal(new String[] { "hi", "spring", "castor" }, writer);
fail(writer);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
結果如下:
~~~
"1.0" encoding="UTF-8"?>
<boolean>trueboolean>"1.0" encoding="UTF-8"?>
9527"1.0" encoding="UTF-8"?>
<float>2.2float>"1.0" encoding="UTF-8"?>
<double>1.11double>"1.0" encoding="UTF-8"?>
lucy"1.0" encoding="UTF-8"?>
<[-b>aGVsbG8gY2FzdG9y"1.0" encoding="UTF-8"?>
abc"1.0" encoding="UTF-8"?>
hispringcastor
~~~
都是類型為節點名稱,值為text。但是這里并沒有出現Map,如果轉換Map需要mapping進行配置。下面再慢慢道來-.-
5、 將xml解組成JavaBean
~~~
/**
* function:將XML內容,解組成JavaBean
* @author hoojo
* @createDate 2011-4-22 下午12:12:14
*/
@Test
public void readXML2Bean() {
String xml = "" +
"
北京
" +
"jackemail" +
"2010-11-22";
reader = new StringReader(xml);
try {
//解組
Account account = (Account) Unmarshaller.unmarshal(Account.class, reader);
fail(account);
} catch (MarshalException e) {
e.printStackTrace();
} catch (ValidationException e) {
e.printStackTrace();
}
}
~~~
結果如下:
~~~
1#jack#email#北京#2010-11-22
~~~
其他的類型,如:map、list、array都不能成功解組。因為這些類型里面有很多系統默認的xml描述。但是利用mapping和自定義JavaBean就可以成功編組和解組了。下面看看mapping是怎么玩轉這些類型的。
### 三、利用mapping配置,編組JavaObject、解組XML
最開始的init方法就提供了mapping,讓我們對mapping這個配置有了大概的了解。下面我們將詳細介紹mapping是個什么:
1、 在此之前我們設置過mapping.xml。如果不設置,肯定是不能轉換成我們想要的XML的。那么,mapping.xml配置文件是怎么配置Account這個對象的呢?
mapping.xml配置如下:
~~~
xml version="1.0" encoding="UTF-8"?>
DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd">
<mapping>
<class name="com.hoo.entity.Account" auto-complete="true">
<map-to xml="Account"/>
<field name="id" type="integer">
<bind-xml name="id" node="attribute" />
</field>
<field name="name" type="string">
<bind-xml name="name" node="element" />
</field>
<field name="email" type="string">
<bind-xml name="email" node="element" />
</field>
<field name="address" type="string">
<bind-xml name="address" node="element" />
</field>
<field name="birthday" type="com.hoo.entity.Birthday">
<bind-xml name="生日" node="element" />
</field>
</class>
<class name="com.hoo.entity.Birthday">
<map-to xml="birthday" />
<field name="birthday" type="string">
<bind-xml name="birthday" node="attribute" />
</field>
</class>
</mapping>
~~~
首先,看看這個xml文檔的根元素是mapping,在mapping中可以配置class。也就是我們要轉換的JavaObject的配置描述了。
class元素的name屬性就是配置的JavaObject的classpath路徑了。
關于class元素的auto-complate屬性,如果這個屬性的值為ture。那么編組后的xml,castor會自動給沒有在mapping配置文件進行配置的屬性自動編組(轉換)到xml中。如果為false,那么在mapping配置文件中出現的屬性將在編組后不現在在編組后的xml中。
map-to就是當前class編組后的xml文檔的節點元素名稱。
field就是描述JavaObject中的屬性,name是Java對象的屬性名稱,type是類型。關于配置的type類型也有規定,你可以參考:[http://www.castor.org/xml-mapping.html的field](http://www.castor.org/xml-mapping.html的field)配置講解。
而field還有其他的屬性配置,如get-method應該是getter方法、set-method應該是setter的方法、has-mehtod應該是hashCode方法,有時候我們不一定要提高getter、setter方法,我們需要用自己的方法名稱來代替setter、getter。如果當前field配置的是集合類型,那么你需要給field元素配置collection屬性。
bind-xml就是綁定(編組)成xml后的xml內容的描述,name就是編組后xml的節點元素名稱,node有2個值,分別是attribute、element。attribute是屬性,它會在節點元素的屬性中顯示,例如:
而element則是單獨的一個元素,例如:2
就這個樣子的。
mapping.xml還可以有其他標簽,如:
導入外部xml文件,可以分多個配置。
好了,先將這么多的mapping方面的內容。我們還是看看實際運行的示例吧,代碼如下:
~~~
/**
* function:將XML內容解組成Java對象
* @author hoojo
* @createDate 2011-4-22 下午12:13:28
*/
@Test
public void bean4Mapping2XML() {
writer = new StringWriter();
try {
//編組
Marshaller mar = new Marshaller(writer);
mar.setMapping(mapping);
mar.marshal(bean);
fail(writer);
//解組
reader = new StringReader(writer.toString());
Unmarshaller unmar = new Unmarshaller(Account.class);
unmar.setMapping(mapping);
Account account = (Account) unmar.unmarshal(reader);
fail(account);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
運行后結果如下:
~~~
"1.0" encoding="UTF-8"?>
"1">jackemail
北京
<生日 birthday="2010-11-22"/>
1#jack#email#北京#2010-11-22
~~~
上面的xml的根節點是Account,這個功勞就來源于mapping配置中的map-to元素,而根節點的id屬性是有field和bind-xml來完成的。當bind-xml的node值為attribute時,就會以屬性的方式顯示。當node為element時,就會像后面name、email一樣,以元素名稱顯示。
再看看上面的mapping文件中的Account的配置,有個auto-complate屬性,如果把這個屬性的值設置成false,會怎么樣?那我們趕緊試試。
沒有發現上面異樣,但是當我們刪除下面配置的filed的時候,就發現有變化了。
結果如下:
~~~
"1.0" encoding="UTF-8"?>
jackemail
北京
<生日 birthday="2010-11-22"/>
0#jack#email#北京#2010-11-22
~~~
發現id沒有顯示在xml中,那么我們再將auto-complate的屬性設置true,會有什么驚喜?
結果如下:
~~~
"1.0" encoding="UTF-8"?>
"1">jackemail
北京
<生日 birthday="2010-11-22"/>
1#jack#email#北京#2010-11-22
~~~
發現id又回來了,但是Account的配置中并沒有配置id的field。這是為什么,其實auto-comlate在上面已經講過了。Castor在編組時會自動將int類型的屬性,顯示在父元素的屬性中。并且JavaObject中有的屬性沒有在mapping配置文件中配置,castor也會自動將其編組在xml中。
下面我們看看map-to配置的用法,map-to的主要屬性是name,也就是我們把當前根元素重命名的名稱。Map-to還有2個屬性可以用,分別是ns-uri、ns-prefix。看名稱就知道它大概的意識,一個是命名空間的uri另一個則是命名空間的前綴。我們給上面mapping加上這兩個屬性看看。
結果如下:
~~~
"1.0" encoding="UTF-8"?>
"http://hoojo.cnblogs.com" id="1">jackemail
北京"2010-11-22"/>
1#jack#email#北京#2010-11-22
~~~
發現了什么?節點元素都帶上了ns-prefix的值,而根元素則有了xml的ns。
2、 將一段XML格式字符串轉換成JavaBean
~~~
@Test
public void readBean4Mapping2XML() {
String xml = "" +
"jackemail
北京
<生日 birthday=/"2010-11-22/"/>";
try {
reader = new StringReader(xml);
Unmarshaller unmar = new Unmarshaller(Account.class);
unmar.setMapping(mapping);
Account account = (Account) unmar.unmarshal(reader);
fail(account);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
運行后結果如下:
~~~
2241#jack#email#北京#2010-11-22
~~~
3、 將XML內容解組成Java的Array
~~~
/**
* function:將XML內容解組成Java的Array
* @author hoojo
* @createDate 2011-4-22 下午12:14:50
*/
@Test
public void array4Mapping2XML() {
writer = new StringWriter();
Account[] acc = new Account[2];
acc[0] = bean;
bean = new Account();
bean.setName("tom");
bean.setId(223);
acc[1] = bean;
AccountArray array = new AccountArray();
array.setAccounts(acc);
try {
Marshaller mar = new Marshaller(writer);
mar.setMapping(mapping);
mar.marshal(array);
fail(writer);
reader = new StringReader(writer.toString());
Unmarshaller unmar = new Unmarshaller(AccountArray.class);
unmar.setMapping(mapping);
array = (AccountArray) unmar.unmarshal(reader);
fail(array.getSize());
fail(array.getAccounts()[0]);
fail(array.getAccounts()[1]);
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
AccountArray的mapping配置如下:
~~~
<class name="com.hoo.entity.AccountArray">
<map-to xml="account-array"/>
<field name="size" type="int" />
<field name="accounts" collection="array" type="com.hoo.entity.Account">
<bind-xml name="accounts" auto-naming="deriveByClass"/>
</field>
</class>
~~~
collection表示是數組,auto-maming有2中值,一種是類driverByClass,另一種則是driverByField是屬性。
運行后,結果如下:
~~~
"1.0" encoding="UTF-8"?>
2"1">jackemail
北京
<生日 birthday="2010-11-22"/>"223">tom
2
1#jack#email#北京#2010-11-22
223#tom#null#null#null
~~~
4、 將Map編組、解組成JavaObject
~~~
/**
* function:xml轉換成Java的Map
* @author hoojo
* @createDate 2011-4-22 下午12:15:18
*/
@Test
public void map4Mapping2XML() {
writer = new StringWriter();
MapBean mapBean = new MapBean();
Map map = new HashMap();
map.put("No1", bean);
bean = new Account();
bean.setName("tom");
bean.setId(223);
map.put("No2", bean);
mapBean.setMap(map);
try {
Marshaller mar = new Marshaller(writer);
mar.setMapping(mapping);
mar.marshal(mapBean);
fail(writer);
reader = new StringReader(writer.toString());
Unmarshaller unmar = new Unmarshaller(MapBean.class);
unmar.setMapping(mapping);
mapBean = (MapBean) unmar.unmarshal(reader);
fail(mapBean.getMap());
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
Mapping配置
~~~
<class name="com.hoo.entity.MapBean">
<field name="map" collection="map">
<bind-xml name="map">
<class name="org.exolab.castor.mapping.MapItem">
<field name="key" type="java.lang.String">
<bind-xml name="key" node="attribute" />
</field>
<field name="value" type="com.hoo.entity.Account">
<bind-xml name="value" auto-naming="deriveByClass"/>
</field>
</class>
</bind-xml>
</field>
</class>
~~~
上面的map配置必須這樣配置,利用*org.exolab.castor.mapping.MapItem*這個class,完成key、value的配置。
結果如下:
~~~
"1.0" encoding="UTF-8"?>
"No2">"223">tom
"No1">"1">jackemail
北京
<生日 birthday="2010-11-22"/>
{No2=223#tom#null#null#null, No1=1#jack#email#北京#2010-11-22}
~~~
5、 JavaList編組、解組XML
~~~
/**
* function:List到XML的相互轉換
* @author hoojo
* @createDate 2011-4-22 下午12:16:04
*/
@SuppressWarnings("unchecked")
@Test
public void listForMapping2XML() {
writer = new StringWriter();
List list = new ArrayList();
list.add(bean);
bean = new Account();
bean.setName("tom");
bean.setId(223);
list.add(bean);
ListBean listBean = new ListBean();
listBean.setList(list);
try {
Marshaller mar = new Marshaller(writer);
mar.setMapping(mapping);
mar.marshal(listBean);
fail(writer);
reader = new StringReader(writer.toString());
Unmarshaller unmar = new Unmarshaller(ListBean.class);
unmar.setMapping(mapping);
listBean = (ListBean) unmar.unmarshal(reader);
fail(listBean.getList().size());
for (Account acc : (List)listBean.getList()) {
fail(acc);
}
} catch (MarshalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
Mapping配置
~~~
<class name="com.hoo.entity.ListBean">
<map-to xml="listBean"/>
<field name="list" collection="arraylist" type="com.hoo.entity.Account">
<bind-xml name="beans" auto-naming="deriveByClass"/>
</field>
</class>
~~~
結果:
~~~
"1.0" encoding="UTF-8"?>
"1">jackemail
北京
<生日 birthday="2010-11-22"/>"223">tom
2
1#jack#email#北京#2010-11-22
223#tom#null#null#null
~~~