2012-01-27 4 views
2

Map<Integer,String>XmlAdapterMap<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; 

} 

지도 유형 :

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<String,Object>Map<Object,Object>에서지도를 바꾸었다. MyMapEntryType 클래스의 Object 속성에 @XmlAnyElement@XmlMixed 주석을 추가했습니다. 주석을 삭제했습니다 (@XmlValue).

내가 코드를 디버깅 할 때,이 줄은 오류 (만세)를 생성하지 않습니다라는 오류에

xyz.put("key0", 1); 

결과 :이 항목을 마샬링하는 시도, 그러나

JAXBContext jc = JAXBContext.newInstance(PropertyBag.class); 

를 :

unable to marshal type `java.lang.Integer` as an element because it is missing an `@XmlRootElement` annotation 
+0

내가 잘못했을 수도 있지만 JAXB가 탐색 유형을 수행 할 수 없다고 생각합니다. 원래의 경우에는 'MyMapEntryType'에 객체가 있습니다. 배열은 객체이고, 맵은 객체이고, 기본 래퍼는 객체이며, 다른 모든 복합 클래스 인스턴스는 객체입니다. JAXB가이 객체를 보는 것만으로 어떤 serializer를 사용할 수 있는지 결정할 수있는 방법은 무엇입니까? "공통"유형의 빌트인 직렬 기가 될 수 있지만 알 수없는 유형에는 체인에 또 다른 어댑터가 필요합니다. –

+0

그래서 비 공통 유형마다 XmlAdapter가 필요합니까? – craig

+0

예. String, Date, List, 모든 프리미티브와 같은 기본 유형은 이미 지원되지만 나머지는 명시 적으로이 방식으로 선언하거나 다른 방식으로 (비공개) 직렬화하는 방법이 필요합니다. 여기서 핵심은 JAXB가 마샬링이 수행되지 않을 때 컨텍스트가 생성 될 때 모든 클래스 (및 가능한 XML 매핑)의 메타 모델을 구축한다는 점입니다. Object 형의 경우 JAXB는 마샬링 할 수있는 방법을 발견 할 방법이 없습니다. 'Object' 뒤에 숨기고 싶은 XML 조각이 다양하다면, 당신은 ['@XmlAnyElement'] (http://stackoverflow.com/questions/9040695) 길을 갈 필요가 있습니다. –

답변

1

나는 주변을 둘러보고 this reply을 발견했습니다. 따라서 사용자 정의 XML < -> DOM 변환기가있는 @XmlAnyElement을 사용하거나 @XmlElementRefs과 함께이 속성에 대해 가능한 클래스가 무엇인지 명시 적으로 지정해야합니다.

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; 

    } 

    ... 

} 

합니다 PropertyBag (옛 성은 푸) 클래스 :

@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; 
    } 

} 
1

나는 내부적으로지도를 사용하여이 문제를 해결 할 수 있었다

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); 

출력 :

<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> 

나는 또한 하위 속성을 직렬화 할 수 있도록하고 싶습니다 : 이상적으로

<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을 가지고 싶습니다처럼 표현 :

... 

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); 

ed :

<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> 

그러나 주석을 사용하여 키 이름을 요소로 변환 할 수 있는지 확신 할 수 없습니다.

관련 문제