自定义地图<对象,对象> XmlAdapter [英] Custom Map<Object,Object> XmlAdapter
问题描述
我正在尝试调整一个 Map< Integer,String>
XmlAdapter
$ C>地图<对象,对象> 。该方法基于以下文章: XmlAdapter - JAXB的秘密武器
测试工具中的这一行生成 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);
}
返回myMapType;
}
@Override
public Map< Object,Object> unmarshal(MyMapType arg0)throws Exception {
HashMap< Object,Object> hashMap = new HashMap< Object,Object>();
(MyMapEntryType myEntryType:arg0.entry){
hashMap.put(myEntryType.key,myEntryType.value);
}
返回hashMap;
}
}
要编组的对象/未组织的对象:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
@XmlJavaTypeAdapter(MyMapAdapter.class)
映射< 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);
//生成NullPointerException
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
marshaller.marshal(foo,System.out);
堆栈跟踪:
code $ 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)
在com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory.create (PropertyFactory.java:95)
在com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl。< init>(ClassBeanInfoImpl.java:145)
在com.sun.xml。 internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:479)
在com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:498)
在com.sun.xml.internal.bind.v2.runtime.property.ArrayElementProperty。< init>(ArrayElementProperty.java:97)
在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)
在java.lang.reflect.Constructor.newInstance(Constructor.java:513)
在com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java: 113)
在com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl。< init>(ClassBeanInfoImpl.java:145)
在com.sun.xml.internal.bind.v2 .runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:479)
在com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:498)
在com.sun .xml.internal.bind.v2.runtime.property.SingleElementNodeProperty。< init>(SingleElementNodeProperty.java:90)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Met hod)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
在java.lang.reflect .Constructor.newInstance(Constructor.java:513)
在com.sun.xml.internal.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:113)
在com.sun .xml.internal.bind.v2.runtime.ClassBeanInfoImpl。< init>(ClassBeanInfoImpl.java:145)
在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.createConte xt(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)
在java.lang.reflect.Method.invoke(Method.java:597)
在javax.xml.bind.ContextFinder.newInstance(ContextFinder。 java:228)
在javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:215)
在javax.xml.bind.ContextFinder.find(ContextFinder.java:414)
在javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:618)
在javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:565)
我将地图从 Map< Object,Object>
更改为 Map< String,Object>
来更好地表示我的需要(我也改变了所有的内部对象来匹配)。我将 @XmlAnyElement
和 @XmlMixed
注释添加到 MyMapEntryType
class;我删除了 @XmlValue
注释。
当我调试代码时,这行不会产生错误(hurray ):
JAXBContext jc = JAXBContext.newInstance(PropertyBag.class);
但是,尝试组织此条目:
xyz.put(key0,1);
结果显示错误:
无法组织类型java.lang.Integer作为元素,因为它缺少一个@ XmlRootElement注释
我可以通过在内部使用Map来解决这个问题:
MyMapEntryType:
public class MyMapEntryType {
@XmlAttribute
public String key;
@XmlValue
public String value;
private MyMapEntryType(){
// 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扩展XmlAdapter< MyMapType,Map< String,Object>> {
@Override
public MyMapType marshal(Map< String,Object> bt)throws异常{
MyMapType myMapType = new MyMapType();对于(Map.Entry< String,Object>条目:bt.entrySet())的
{
MyMapEntryType myMapEntryType = new MyMapEntryType(entry.getKey(),entry.getValue ()的ToString());
myMapType.entry.add(myMapEntryType);
}
返回myMapType;
}
...
}
PropertyBag(nee Foo)类:
@XmlRootElement(name =PropertyBag)
public class PropertyBag {
private Map< String,Object>地图;
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;
}
}
线束:
Map< String,Object> parent = new HashMap< String,Object>();
parent.put(SI_EMAIL_ADDRESS,foo@bar.edu);
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);
输出:
code><&的PropertyBag GT;
<属性>
< 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>
<属性key =SI_INTEGER> 23456< / Property>
< Property key =SI_GUID> ATQJj1RvgVlLqDqP_VOGltM< / Property>
< Property key =SI_EMAIL_ADDRESS> foo@bar.edu< / Property>
< /属性>
< / PropertyBag>
理想情况下,我希望将XML表示为:
<属性>
< 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> foo@bar.edu< / SI_EMAIL_ADDRESS>
< /属性>
我也希望能够序列化子属性:
...
映射< 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);
哪些代表:
<性状>
< 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> foo@bar.edu< / SI_EMAIL_ADDRESS>
< SI_LIST>
<! - 数字元素是非法的;重构 - >
< 1 33217< / 1>
< 2> 36351< / 2>
< SI_TOTAL> 2< / SI_TOTAL>
< / SI_LIST>
< /属性>
但我不确定我可以使用注释将键名转换为元素。 / p>
I'm trying to adapt a Map<Integer,String>
XmlAdapter
to one that supports Map<Object,Object>
. The approach is based on this article: XmlAdapter - JAXB's Secret Weapon
This line in the testing harness generates NullPointerException
:
JAXBContext jc = JAXBContext.newInstance(Foo.class);
If I change the harness and MapAdapter/MapEntry to be T<Integer,String>
, the code works as expected.
What am I missing? Can the Object
type be serialized or does it need to be cast as another, less abstract class? If so, I would think that I would encounter this error in the marshal()
method, but it never seems to reach this point (at least in Netbean's debugger).
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;
}
}
Object to be marshalled/unmarshalled:
@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;
}
}
Testing harness:
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);
Stack trace:
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)
I changed the Map from Map<Object,Object>
to Map<String,Object>
to better represent my needs (I also changed all of the internal objects to match this). I added @XmlAnyElement
and @XmlMixed
annotations to the Object property of the MyMapEntryType
class; I removed the @XmlValue
annotation.
When I debug the code, this line does NOT produce an error (hurray):
JAXBContext jc = JAXBContext.newInstance(PropertyBag.class);
However, attempting to marshal this entry:
xyz.put("key0", 1);
Results in an error that reads:
unable to marshal type `java.lang.Integer` as an element because it is missing an `@XmlRootElement` annotation
I was able to solve this by using a Map internally:
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 class:
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 (nee Foo) class:
@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;
}
}
Harness:
Map<String, Object> parent = new HashMap<String, Object>();
parent.put("SI_EMAIL_ADDRESS", "foo@bar.edu");
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);
Output:
<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">foo@bar.edu</Property>
</Properties>
</PropertyBag>
Ideally, I'd like to have the XML represented like:
<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>foo@bar.edu</SI_EMAIL_ADDRESS>
</Properties>
I'd also like to be able to serialize subproperties:
...
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);
Which would be represented:
<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>foo@bar.edu</SI_EMAIL_ADDRESS>
<SI_LIST>
<!-- numeric elements are illegal; refactor -->
<1>33217</1>
<2>36351</2>
<SI_TOTAL>2</SI_TOTAL>
</SI_LIST>
</Properties>
But I'm not certain that I can use annotation to convert a key name to an element.
这篇关于自定义地图<对象,对象> XmlAdapter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!