具有讽刺意味的是,可能很难在JAXB(JSR-222)中映射java.util.Map类? 在这篇文章中,我将介绍一些使其变得更容易的项目。
Java模型
以下是我们将用于此示例的Java模型。
顾客
Customer类具有Map类型的属性。 我之所以选择此Map,是因为键是一个域对象,而值是一个域对象。
package blog.map;
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;
}
}
地址
Address类只是典型的POJO。
package blog.map;
public class Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
示范代码
在下面的演示代码中,我们将创建一个Customer实例并填充其Map属性。 然后,将其封送为XML。
package blog.map;
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);
}
}
用例#1 –默认表示
以下是与我们的域模型相对应的XML示例。 我们看到Map中的每个项目都有包裹在entry元素中的key和value元素。
<?xml version='1.0' encoding='UTF-8'?>
<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>
用例2 –重命名元素
JAXB参考实现使用@XmlElementWrapper批注来重命名与Map属性相对应的元素(我们已在EclipseLink 2.4.2和2.5.0中向MOXy添加了此支持)。 在MOXy的早期版本中,应使用@XmlElement批注。
顾客
我们将使用@XmlElementWrapper批注将与addressMap属性对应的元素重命名为address 。
package blog.map;
import java.util.*;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Customer {
private Map<String, Address> addressMap = new HashMap<String, Address>();
@XmlElementWrapper(name='addresses')
public Map<String, Address> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, Address> addressMap) {
this.addressMap = addressMap;
}
}
输出量
现在我们看到的是,addressMap元素已被重新命名为地址 。
<?xml version='1.0' encoding='UTF-8'?>
<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>
用例#3 –添加命名空间资格
在此用例中,我们将研究将名称空间限定应用于属性类型为java.util.Map的类的影响。 有一个与Map属性的名称空间限定有关的MOXy错误,已在EclipseLink 2.4.2和2.5.0中修复(请参见: http : //bugs.eclipse.org/399297 )。
包装信息
我们将使用包级别@XmlSchema批注来指定属于该包中的类的所有字段/属性都应使用http://www.example.com命名空间进行限定(请参阅: JAXB&Namespaces )。
@XmlSchema(
namespace='http://www.example.com',
elementFormDefault=XmlNsForm.QUALIFIED)
package blog.map;
import javax.xml.bind.annotation.*;
输出量
我们看到与Customer和Address类相对应的元素是名称空间限定的,但与Map类相对应的元素则不是。 这是因为Map类来自java.util包,因此我们在包级别@XmlSchema注释中指定的信息不适用。
<?xml version='1.0' encoding='UTF-8'?>
<ns2:customer xmlns:ns2='http://www.example.com'>
<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>
用例#4 –使用XmlAdapter修复命名空间资格
我们可以使用XmlAdapter来调整前一个用例的名称空间限定。
XmlAdapter(MapAdapter)
XmlAdapter机制允许您将一个类转换为另一个类,以影响映射(请参阅: XmlAdapter – JAXB的Secret Weapon )。 为了获得适当的名称空间限定,我们将使用XmlAdapter将Map转换为域模型包中的对象。
package blog.map;
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;
}
}
顾客
@XmlJavaTypeAdapter批注用于在Map属性上指定XmlAdapter 。 请注意,在应用XmlAdaper的情况下,我们需要将@XmlElementWrapper批注更改为@XmlElement (证明应该使用@XmlElement来注释Map属性的元素)。
package blog.map;
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Customer {
private Map<String, Address> addressMap = new HashMap<String, Address>();
@XmlJavaTypeAdapter(MapAdapter.class)
@XmlElement(name='addresses')
public Map<String, Address> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, Address> addressMap) {
this.addressMap = addressMap;
}
}
输出量
现在,XML输出中的所有元素都已使用http://www.example.com命名空间进行了限定。
<?xml version='1.0' encoding='UTF-8'?>
<customer xmlns='http://www.example.com'>
<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>
参考: Java XML和JSON绑定博客中我们的JCG合作伙伴 Blaise Doughan的JAXB和java.util.Map 。
翻译自: https://www.javacodegeeks.com/2013/03/jaxb-and-java-util-map.html