我試圖將Map<Integer,String>
XmlAdapter
修改爲支持Map<Object,Object>
的映射。該方法是基於這篇文章:XmlAdapter - JAXB's Secret Weapon自定義映射<Object,Object> XmlAdapter
這條線的測試工具生成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;
}
的MapType:
public class MyMapType {
public List<MyMapEntryType> entry = new ArrayList<MyMapEntryType>();
}
MapAdapter:
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<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'中有對象。數組是對象,映射是對象,原始包裝器是對象,其他任何複合類實例都是對象。 JAXB如何通過查看此對象來確定要使用的序列化程序?它可以是「普通」類型的內置序列化器,但未知類型需要另外一個鏈中的適配器。 – 2012-01-28 11:45:30
所以,我需要一個XmlAdapter爲每個非常見的類型? – craig 2012-01-28 23:23:59
是的。基本類型如String,Date,List,所有原語已經被支持,但其餘的你需要以這種或那種方式明確地聲明如何(去)序列化它們。這裏的重點是JAXB在創建上下文時構建所有類的元模型(以及可能的XML映射),而不是在編組完成時。對於'Object'類型,JAXB無法發現可能的方法來進行編組。如果你想隱藏在Object後面的那段XML是不同的,那麼你需要去['@ XmlAnyElement'](http://stackoverflow.com/questions/9040695)的方式。 – 2012-01-29 08:53:03