2012-01-27 53 views
2

我試圖將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 
+0

我可能是錯的,但我認爲JAXB不能做類型探索。在你原來的情況下,你在'MyMapEntryType'中有對象。數組是對象,映射是對象,原始包裝器是對象,其他任何複合類實例都是對象。 JAXB如何通過查看此對象來確定要使用的序列化程序?它可以是「普通」類型的內置序列化器,但未知類型需要另外一個鏈中的適配器。 – 2012-01-28 11:45:30

+0

所以,我需要一個XmlAdapter爲每個非常見的類型? – craig 2012-01-28 23:23:59

+0

是的。基本類型如String,Date,List,所有原語已經被支持,但其餘的你需要以這種或那種方式明確地聲明如何(去)序列化它們。這裏的重點是JAXB在創建上下文時構建所有類的元模型(以及可能的XML映射),而不是在編組完成時。對於'Object'類型,JAXB無法發現可能的方法來進行編組。如果你想隱藏在Object後面的那段XML是不同的,那麼你需要去['@ XmlAnyElement'](http://stackoverflow.com/questions/9040695)的方式。 – 2012-01-29 08:53:03

回答

1

我只是看看周圍,並遇到this reply。因此,您可以使用的是@XmlAnyElement和自定義XML < - > DOM轉換器,或者您需要明確指定@XmlElementRefs該屬性的可能類別。

1

我能夠通過使用地圖內部來解決這個問題:

MyMapEntryType:

public class MyMapEntryType { 

    @XmlAttribute 
    public String key; 

    @XmlValue 
    public String value; 

    private MyMapEntryType() { 
     //Required by JAXB 
    } 

    public MyMapEntryType(String key, String value) { 
     this.key = key; 
     this.value = value; 
    } 

} 

MyMapType類:

public class MyMapType { 

    @XmlElement(name="Property") 
    public List<MyMapEntryType> entry = new ArrayList<MyMapEntryType>(); 

} 

public class MyMapAdapter extends XmlAdapter<MyMapType, Map<String,Object>> { 

    @Override 
    public MyMapType marshal(Map<String,Object> bt) throws Exception { 

     MyMapType myMapType = new MyMapType(); 

     for(Map.Entry<String,Object> entry : bt.entrySet()) { 

      MyMapEntryType myMapEntryType = new MyMapEntryType(entry.getKey(), entry.getValue().toString()); 
      myMapType.entry.add(myMapEntryType); 

     } 

     return myMapType; 

    } 

    ... 

} 

屬性包(NEE美孚)類:

@XmlRootElement(name="PropertyBag") 
public class PropertyBag { 

    private Map<String,Object> map; 

    public PropertyBag() { 
     map = new HashMap<String,Object>(); 
    } 

    @XmlJavaTypeAdapter(MyMapAdapter.class) 
    @XmlElement(name="Properties") 
    public Map<String,Object> getMap() { 
     return map; 
    } 

    public void setMap(Map<String,Object> map) { 
     this.map = map; 
    } 

} 

Harnes S:

 Map<String, Object> parent = new HashMap<String, Object>(); 
     parent.put("SI_EMAIL_ADDRESS", "[email protected]"); 
     parent.put("SI_DATE", new Date()); 
     parent.put("SI_GUID", "ATQJj1RvgVlLqDqP_VOGltM"); 
     parent.put("SI_BOOLEAN", true); 
     parent.put("SI_INT", 1318); 
     parent.put("SI_LONG", new Long(123456789)); 
     parent.put("SI_INTEGER", new Integer(23456)); 


     PropertyBag bag = new PropertyBag(); 
     bag.setMap(parent); 

     JAXBContext jc = JAXBContext.newInstance(PropertyBag.class);    
     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(bag, System.out); 

輸出:

<PropertyBag> 
    <Properties> 
     <Property key="SI_INT">1318</Property> 
     <Property key="SI_DATE">Wed Feb 01 22:18:35 EST 2012</Property> 
     <Property key="SI_BOOLEAN">true</Property> 
     <Property key="SI_LONG">123456789</Property> 
     <Property key="SI_INTEGER">23456</Property> 
     <Property key="SI_GUID">ATQJj1RvgVlLqDqP_VOGltM</Property> 
     <Property key="SI_EMAIL_ADDRESS">[email protected]</Property> 
    </Properties> 
</PropertyBag> 

理想情況下,我想有XML表示如下:

<Properties> 
    <SI_INT>1318</SI_INT> 
    <SI_DATE>Wed Feb 01 22:18:35 EST 2012</SI_DATE> 
    <SI_BOOLEAN>true</SI_BOOLEAN> 
    <SI_LONG>123456789</SI_LONG> 
    <SI_INTEGER>23456</SI_INTEGER> 
    <SI_GUID>ATQJj1RvgVlLqDqP_VOGltM</SI_GUID> 
    <SI_EMAIL_ADDRESS>[email protected]</SI_EMAIL_ADDRESS> 
</Properties> 

我還希望能夠連載子屬性:

... 

Map<String,Object> child = new HashMap<String,Object>(); 
child.put("1", 33217); 
child.put("2", 36351); 
child.put("SI_TOTAL", 2); 
parent.put("SI_LIST", child); 

這將代表編輯:

<Properties> 
    <SI_INT>1318</SI_INT> 
    <SI_DATE>Wed Feb 01 22:18:35 EST 2012</SI_DATE> 
    <SI_BOOLEAN>true</SI_BOOLEAN> 
    <SI_LONG>123456789</SI_LONG> 
    <SI_INTEGER>23456</SI_INTEGER> 
    <SI_GUID>ATQJj1RvgVlLqDqP_VOGltM</SI_GUID> 
    <SI_EMAIL_ADDRESS>[email protected]</SI_EMAIL_ADDRESS> 
    <SI_LIST> 
     <!-- numeric elements are illegal; refactor --> 
     <1>33217</1> 
     <2>36351</2> 
     <SI_TOTAL>2</SI_TOTAL> 
    </SI_LIST> 
</Properties> 

但我不確定,我可以使用註釋轉換密鑰名稱爲元素。