JAXB 序列化 java.util.Map

来源:互联网 发布:java spring框架是什么 编辑:程序博客网 时间:2024/06/04 18:24

使用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.*; @XmlRootElementpublic class Customer {     private Map<String, Address> addressMap = new HashMap<String, Address>();     public Map<String, Address> getAddressMap() {        return addressMap;    }     public void setAddressMap(Map<String, Address> addressMap) {        this.addressMap = addressMap;    } }

Adress类是一个纯POJO类,定义如下

package cn.outofmemory.jaxb; public class Address {     private String street;     public String getStreet() {        return street;    }     public void setStreet(String street) {        this.street = street;    } }

序列化入口类,此类初始化了Customer对象,并将此对象的jaxb序列化结果,输出到System.out流中,代码如下:

package cn.outofmemory.jaxb; import javax.xml.bind.*; public class Demo {     public static void main(String[] args) throws Exception {        JAXBContext jc = JAXBContext.newInstance(Customer.class);                 Address billingAddress = new Address();        billingAddress.setStreet("1 A Street");                 Address shippingAddress = new Address();        shippingAddress.setStreet("2 B Road");                 Customer customer = new Customer();        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")    public Map<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:customer xmlns: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; public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, Address>> {         public static class AdaptedMap {                 public List<Entry> entry = new ArrayList<Entry>();      }         public static class Entry {                 public String key;                 public Address value;       }     @Override    public Map<String, Address> unmarshal(AdaptedMap adaptedMap) throws Exception {        Map<String, Address> map = new HashMap<String, Address>();        for(Entry entry : adaptedMap.entry) {            map.put(entry.key, entry.value);        }        return map;    }     @Override    public AdaptedMap marshal(Map<String, Address> map) throws Exception {        AdaptedMap adaptedMap = new AdaptedMap();        for(Map.Entry<String, Address> mapEntry : map.entrySet()) {            Entry entry = new Entry();            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")public Map<String, Address> getAddressMap() {    return addressMap;}

这样序列化类型就都属于我们自己的包了,会有统一的命名空间,我们看下输出xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <customer xmlns="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

0 0
原创粉丝点击