【发布时间】:2012-03-19 17:34:21
【问题描述】:
我正在尝试将Map<Integer,String> XmlAdapter 改编为支持Map<Object,Object> 的版本。该方法基于这篇文章:XmlAdapter - JAXB's Secret Weapon
测试工具中的这一行生成NullPointerException:
JAXBContext jc = JAXBContext.newInstance(Foo.class);
如果我将线束和 MapAdapter/MapEntry 更改为 T<Integer,String>,代码将按预期工作。
我错过了什么? Object 类型可以序列化还是需要转换为另一个不那么抽象的类?如果是这样,我认为我会在marshal() 方法中遇到这个错误,但它似乎永远不会达到这一点(至少在Netbean 的调试器中)。
MapEntryType:
public class MyMapEntryType {
@XmlAttribute
public Object key;
@XmlValue
public Object value;
}
地图类型:
public class MyMapType {
public List<MyMapEntryType> entry = new ArrayList<MyMapEntryType>();
}
地图适配器:
public final class MyMapAdapter extends
XmlAdapter<MyMapType,Map<Object, Object>> {
@Override
public MyMapType marshal(Map<Object, Object> arg0) throws Exception {
MyMapType myMapType = new MyMapType();
for(Entry<Object, Object> entry : arg0.entrySet()) {
MyMapEntryType myMapEntryType = new MyMapEntryType();
myMapEntryType.key = entry.getKey();
myMapEntryType.value = entry.getValue();
myMapType.entry.add(myMapEntryType);
}
return myMapType;
}
@Override
public Map<Object, Object> unmarshal(MyMapType arg0) throws Exception {
HashMap<Object, Object> hashMap = new HashMap<Object, Object>();
for(MyMapEntryType myEntryType : arg0.entry) {
hashMap.put(myEntryType.key, myEntryType.value);
}
return hashMap;
}
}
要编组/解组的对象:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
@XmlJavaTypeAdapter(MyMapAdapter.class)
Map<Object, Object> map = new HashMap<Object, Object>();
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
}
测试工具:
Map<Object,Object> xyz = new HashMap<Object,Object>();
xyz.put("key0", "value0");
xyz.put("key1", "value1");
Foo foo = new Foo();
foo.setMap(xyz);
//generates NullPointerException
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
堆栈跟踪:
java.lang.NullPointerException
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:154)
at com.sun.xml.internal.bind.v2.runtime.property.ValueProperty.<init>(ValueProperty.java:66)
at com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:95)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:145)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:479)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:498)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayElementProperty.<init>(ArrayElementProperty.java:97)
at com.sun.xml.internal.bind.v2.runtime.property.ArrayElementNodeProperty.<init>(ArrayElementNodeProperty.java:47)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:113)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:145)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:479)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:498)
at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.<init>(SingleElementNodeProperty.java:90)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:113)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:145)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:479)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:305)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1100)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:143)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:110)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:228)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:215)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:414)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:618)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:565)
我将 Map 从 Map<Object,Object> 更改为 Map<String,Object> 以更好地代表我的需求(我还更改了所有内部对象以匹配此)。我在MyMapEntryType类的Object属性中添加了@XmlAnyElement和@XmlMixed注解;我删除了@XmlValue 注释。
当我调试代码时,这一行不会产生错误(万岁):
JAXBContext jc = JAXBContext.newInstance(PropertyBag.class);
但是,尝试编组此条目:
xyz.put("key0", 1);
导致错误如下:
unable to marshal type `java.lang.Integer` as an element because it is missing an `@XmlRootElement` annotation
【问题讨论】:
-
我可能错了,但我认为 JAXB 无法进行类型探索。在您原来的情况下,您在
MyMapEntryType中有对象。数组是对象,map 是对象,原始包装器是对象,任何其他复合类实例都是对象。 JAXB 如何仅通过查看此对象来确定要使用的序列化程序?它可以是“常见”类型的内置序列化程序,但未知类型需要链中的另一个适配器。 -
那么,我需要一个 XmlAdapter 来为每种非常见类型提供服务吗?
-
是的。基本类型,如 String、Date、List,所有原语都已受支持,但对于其余的,您需要以这种方式或其他方式显式声明如何(反)序列化它们。这里的要点是 JAXB 在创建上下文时构建所有类(和可能的 XML 映射)的元模型,而不是在编组完成时。对于
Object类型,JAXB 无法发现可能的编组方式。如果要隐藏在Object后面的XML 是变化的,那么你需要走@XmlAnyElement的方式。
标签: java map jaxb marshalling