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.*;
 
@XmlRootElement
public 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值