问题描述
我有一个 Map<String, String>.
每个人的第一个想法是将其转换为 List<Pair<String,String>> (Pair 是一个自定义类).
I have a Map<String, String>.
The first idea everyone has is to convert it to a List<Pair<String,String>> (Pair being a custom class).
我试过这样的 @XmlAdapter:
public class MapPropertiesAdapter extends XmlAdapter<List<Property>, Map<String,String>> { ... }
但我使用的 JAXB impl Eclipse MOXy 以 ClassCastException 告终——无法将 HashMap 转换为 Collection".
But Eclipse MOXy, the JAXB impl I use, ended up with a ClassCastException - "can't convert HashMap to Collection".
JAXB 是否支持这种转换?还是我忽略了一些解释为什么不是的文档部分?
Is this conversion supported by JAXB? Or did I overlook some documentation part which explains why it isn't?
PS:我想得到这样的 XML:
PS: I wanted to get XML like this:
<properties> <property name="protocol"/> <property name="marshaller"/> <property name="unmarshaller"/> <property name="timeout"/> ... </properties>
我明白了,只需要使用一个中间类.也描述在在 XMLCompositeObjectMappingNodeValue.marshalSingleValue(XMLCompositeObjectMappingNodeValue.java:161) 中处理 NPEp>
I got it, only had to use an intermediate class. Also described at Handle NPE in XMLCompositeObjectMappingNodeValue.marshalSingleValue( XMLCompositeObjectMappingNodeValue.java:161)
推荐答案
不要将 Map 适配为 List,而应将其适配为具有列表属性.
Instead of adapting the Map to a List, you should adapt it to an object that has a List property.
XmlAdapter (MapPropertiesAdapter)
import java.util.*; import java.util.Map.Entry; import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.XmlAdapter; public class MapPropertiesAdapter extends XmlAdapter<MapPropertiesAdapter.AdaptedProperties, Map<String, String>>{ public static class AdaptedProperties { public List<Property> property = new ArrayList<Property>(); } public static class Property { @XmlAttribute public String name; @XmlValue public String value; } @Override public Map<String, String> unmarshal(AdaptedProperties adaptedProperties) throws Exception { if(null == adaptedProperties) { return null; } Map<String, String> map = new HashMap<String, String>(adaptedProperties.property.size()); for(Property property : adaptedProperties.property) { map.put(property.name, property.value); } return map; } @Override public AdaptedProperties marshal(Map<String, String> map) throws Exception { if(null == map) { return null; } AdaptedProperties adaptedProperties = new AdaptedProperties(); for(Entry<String,String> entry : map.entrySet()) { Property property = new Property(); property.name = entry.getKey(); property.value = entry.getValue(); adaptedProperties.property.add(property); } return adaptedProperties; } }
域模型(根)
下面是一个带有 Map 属性的模型对象.@XmlJavaTypeAdapter 注解用于指定XmlAdapter.
Below is a model object with a Map property. The @XmlJavaTypeAdapter annotation is used to specify the XmlAdapter.
import java.util.Map; import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { @XmlJavaTypeAdapter(MapPropertiesAdapter.class) private Map<String, String> properties; }
演示
import java.io.File; import javax.xml.bind.*; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Root.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); File xml = new File("src/forum17024050/input.xml"); Root root = (Root) unmarshaller.unmarshal(xml); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, System.out); } }
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?> <root> <properties> <property name="A">a</property> <property name="B">b</property> </properties> </root>