1. 程式人生 > >JAXB 序列化 java.util.Map

JAXB 序列化 java.util.Map

使用JAXB序列化java.util.Map介面可能會遇到一些問題,本文通過幾種方式來做map的序列化,包括不做任何處理的序列化、修改節點名稱、新增xml名稱空間、使用XmlAdapter統一名稱空間。

首先介紹下序列化涉及到的幾個類:

Customer類包含一個Map的屬性,Map的key型別是String型別,而value型別是我們自定義的POJO型別。其程式碼如下:

package cn.outofmemory.jaxb;import java.util.*;import javax.xml.bind.annotation.*;@XmlRootElementpublicclassCustomer
{privateMap<String,Address> addressMap =newHashMap<String,Address>();publicMap<String,Address> getAddressMap(){return addressMap;}publicvoid setAddressMap(Map<String,Address> addressMap){this.addressMap = addressMap;}}

Adress類是一個純POJO類,定義如下

package cn.outofmemory.jaxb;public
classAddress{privateString street;publicString getStreet(){return street;}publicvoid setStreet(String street){this.street = street;}}

序列化入口類,此類初始化了Customer物件,並將此物件的jaxb序列化結果,輸出到System.out流中,程式碼如下:

package cn.outofmemory.jaxb;import javax.xml.bind.*;publicclassDemo{publicstaticvoid main(String[] args
)throwsException{JAXBContext jc =JAXBContext.newInstance(Customer.class);Address billingAddress =newAddress();         billingAddress.setStreet("1 A Street");Address shippingAddress =newAddress();         shippingAddress.setStreet("2 B Road");Customer customer =newCustomer();         customer.getAddressMap().put("billing", billingAddress);         customer.getAddressMap().put("shipping", shippingAddress);Marshaller marshaller = jc.createMarshaller();         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);         marshaller.marshal(customer,System.out);}}

預設的jaxb序列化 

下面我們看下不做任何設定的預設序列化結果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<addressMap>
<entry>
<key>shipping</key>
<value>
<street>2 B Road</street>
</value>
</entry>
<entry>
<key>billing</key>
<value>
<street>1 A Street</street>
</value>
</entry>
</addressMap>
</customer>

可以看到預設情況下map被序列化成一個一個的entry節點,每個entry節點都有key和value子節點。

修改map屬性的節點名稱

下面我們修改下addressMap節點的名字,需要使用 @XmlElementWrapper 註解,這個註解應該新增到getAddressMap方法上,如下修改後的Csutomer getAdressMap方法的程式碼:

@XmlElementWrapper(name ="addresses")publicMap<String,Address> getAddressMap(){return addressMap;}

這樣修改之後的輸出如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<addresses>
<entry>
<key>shipping</key>
<value>
<street>2 B Road</street>
</value>
</entry>
<entry>
<key>billing</key>
<value>
<street>1 A Street</street>
</value>
</entry>
</addresses>
</customer>

jaxb新增xml名稱空間

下面我們再看下如何使用JAXB控制xml的namespace,我們需要在package-info.java檔案中給package新增如下註解:

@XmlSchema(namespace="http://outofmemory.cn",
	elementFormDefault=XmlNsForm.QUALIFIED)package cn.outofmemory.jaxb;import javax.xml.bind.annotation.*;

XmlSchema註解指定了包中jaxb序列化的名稱空間。

我們可以再執行下Demo看下新增名稱空間之後的輸出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:customerxmlns:ns2="http://outofmemory.cn">
<ns2:addresses>
<entry>
<key>shipping</key>
<value>
<ns2:street>2 B Road</ns2:street>
</value>
</entry>
<entry>
<key>billing</key>
<value>
<ns2:street>1 A Street</ns2:street>
</value>
</entry>
</ns2:addresses>
</ns2:customer>

從上面的輸出可以看到Customer類和Adress類的節點和屬性節點都添加了ns2的名稱空間限定,而Map類相關的都沒有新增名稱空間限定,這是因為Map屬於java.util包,這個包中沒有名稱空間限定的註解修飾。

使用XmlAdapter統一jaxb序列化後的xml名稱空間

下面我們通過XmlAdapter類實現統一的名稱空間限定。

使用XmlAdapter可以轉換指定屬性的jaxb序列化方式,我們自定義的XmlAdapter屬於我們自己所建立的包,而在這個包上是有名稱空間註解修飾的。

package cn.outofmemory.jaxb;import java.util.*;import javax.xml.bind.annotation.adapters.XmlAdapter;publicclassMapAdapterextendsXmlAdapter<MapAdapter.AdaptedMap,Map<String,Address>>{publicstaticclassAdaptedMap{publicList<Entry> entry =newArrayList<Entry>();}publicstaticclassEntry{publicString key;publicAddress value;}@OverridepublicMap<String,Address> unmarshal(AdaptedMap adaptedMap)throwsException{Map<String,Address> map =newHashMap<String,Address>();for(Entry entry : adaptedMap.entry){
            map.put(entry.key, entry.value);}return map;}@OverridepublicAdaptedMap marshal(Map<String,Address> map)throwsException{AdaptedMap adaptedMap =newAdaptedMap();for(Map.Entry<String,Address> mapEntry : map.entrySet()){Entry entry =newEntry();
            entry.key = mapEntry.getKey();
            entry.value = mapEntry.getValue();
            adaptedMap.entry.add(entry);}return adaptedMap;}}

XmlAdapter是一個泛型的抽象類,我們需要自己實現marshal和unmarshal方法。在我們的自定義XmlAdapter中我們將Map的鍵值對轉換成我們自定義的Entry類例項,而Entry類所在包是有名稱空間註解修飾的。

然後需要將MapAdapter通過XmlJavaTypeAdapter註解新增到Customer的getAddressMap()方法上,如下程式碼示例:

@XmlJavaTypeAdapter(MapAdapter.class)@XmlElement(name="addresses")publicMap<String,Address> getAddressMap(){return addressMap;}

這樣序列化型別就都屬於我們自己的包了,會有統一的名稱空間,我們看下輸出xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customerxmlns="http://outofmemory.cn">
<addresses>
<entry>
<key>shipping</key>
<value>
<street>2 B Road</street>
</value>
</entry>
<entry>
<key>billing</key>
<value>
<street>1 A Street</street>
</value>
</entry>
</addresses>
</customer>

可以看到最後輸出的xml是統一到一個預設的名稱空間中了。

轉:http://outofmemory.cn/java/jaxb/jaxb-and-java-util-map