I am trying to unmarshall an ArrayList of a generic class called Key.
我试图解组一个名为Key的泛型类的ArrayList。
the Key has setValue() method which recieves a generic parameter.
Key具有setValue()方法,该方法接收泛型参数。
Key class
@XMLRootElement(name = "Key")
public class Key<T>{
@XMLElement(name = "Key")
public setKey(T value){
this.value = value
}
}
The specific ArrayList
@XMLElementWrapper(name = "Keys")
@XMLElement(name = "Key")
public setKeys(ArrayList<Key> keys){
this.keys = keys;
}
This part of the XML file
<Keys>
<Key>2</Key>
</Keys>
Running the code would create the ArrayList and WILL have a single Key object in it. But the Key would be Null.
运行代码将创建ArrayList,并且WILL中将包含一个Key对象。但是Key将是Null。
(Ive tried debugging and could notice that it does not call the setKey() setter of the class)
(我已经尝试过调试,可能会注意到它没有调用类的setKey()setter)
Anything to do with the fact it's generic? Thanks in advance.
它与通用的事实有什么关系?提前致谢。
EDIT
In the past day ive debugged this alot, i can say now that the problem is with the fact that after instantiating the ArrayList, while creating each Key per Key Tag in the XML, the unmarshaller uses the Key's empty constructor and just NEVER calls the setter of it, therefore i have an ArrayList containing Keys which their 'value' data member is null
.
在过去的一天,我调试了很多,我现在可以说,问题在于,在实例化ArrayList之后,在XML中创建每个Key Key的每个Key时,unmarshaller使用Key的空构造函数并且永远不会调用setter因此,我有一个包含键的ArrayList,其'value'数据成员为null。
Can anyone please explain what am I doing wrong? Why does the setter not getting called?
任何人都可以解释我做错了什么?为什么setter没有被调用?
Thank you.
1 个解决方案
#1
0
You are probably out of luck. How is the unmarshaller supposed to know that 2 is an integer and not a double or a long or a timestamp or some other class with a custom adapter that can parse 2 into itself.
你可能运气不好。 unmarshaller如何知道2是一个整数而不是一个double或long或一个时间戳或一些其他类可以解析2自身的自定义适配器。
The annotations you want are basically below (minus the @XmlJavaTypeAdapter which I will explain in a moment) but if you try and run that code without the adapter you will get a NullPointerException because JAXB cannot handle the @XmlValue annotation on an Object (which is how it treats T). The reason JAXB cannot handle it is because it has no way of knowing what the object is.
你想要的注释基本上是下面的(减去我稍后会解释的@XmlJavaTypeAdapter)但是如果你尝试在没有适配器的情况下运行该代码,你将得到NullPointerException,因为JAXB无法处理对象上的@XmlValue注释(这是它如何对待T)。 JAXB无法处理它的原因是因为它无法知道对象是什么。
Now, if you have your own custom rules for determining the type of T (e.g. when coming from XML T is always an Integer or T is an Integer if it doesn't contain a '.' and a Double otherwise) then you can implement your own logic using an adapter which is what I've demonstrated below (I used the second rule).
现在,如果您有自己的自定义规则来确定T的类型(例如,当来自XML T时,总是一个整数,如果它不包含'。'而T是整数,那么你可以实现)你使用适配器的逻辑,这是我在下面演示的(我使用了第二条规则)。
@XmlRootElement(name="root")
public class SO {
private List<Key<?>> keys;
@XmlElementWrapper(name="Keys")
@XmlElement(name="Key")
public void setKeys(List<Key<?>> keys) {
this.keys = keys;
}
public List<Key<?>> getKeys() {
return keys;
}
@XmlType
public static class Key<T> {
private T val;
@XmlValue
@XmlJavaTypeAdapter(ToStringAdapter.class)
public void setKey(T val) {
this.val = val;
}
public String toString() {
return "Key(" + val + ")";
}
}
public static class ToStringAdapter extends XmlAdapter<String, Object> {
@Override
public Object unmarshal(String v) throws Exception {
if(v.contains(".")) {
return Double.parseDouble(v);
} else {
return Integer.parseInt(v);
}
}
@Override
public String marshal(Object v) throws Exception {
return v.toString(); //Will never be called anyway so you could also throw an exception here
}
}
private static final String XML_INT = "<root><Keys><Key>2</Key></Keys></root>";
private static final String XML_DOUBLE = "<root><Keys><Key>2.7</Key></Keys></root>";
public static void main(String [] args) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(Key.class, SO.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
SO so = (SO) unmarshaller.unmarshal(new StringReader(XML_INT));
System.out.print(so.keys);
System.out.println(" " + so.keys.get(0).val.getClass().getSimpleName());
so = (SO) unmarshaller.unmarshal(new StringReader(XML_DOUBLE));
System.out.print(so.keys);
System.out.println(" " + so.keys.get(0).val.getClass().getSimpleName());
}
}
#1
0
You are probably out of luck. How is the unmarshaller supposed to know that 2 is an integer and not a double or a long or a timestamp or some other class with a custom adapter that can parse 2 into itself.
你可能运气不好。 unmarshaller如何知道2是一个整数而不是一个double或long或一个时间戳或一些其他类可以解析2自身的自定义适配器。
The annotations you want are basically below (minus the @XmlJavaTypeAdapter which I will explain in a moment) but if you try and run that code without the adapter you will get a NullPointerException because JAXB cannot handle the @XmlValue annotation on an Object (which is how it treats T). The reason JAXB cannot handle it is because it has no way of knowing what the object is.
你想要的注释基本上是下面的(减去我稍后会解释的@XmlJavaTypeAdapter)但是如果你尝试在没有适配器的情况下运行该代码,你将得到NullPointerException,因为JAXB无法处理对象上的@XmlValue注释(这是它如何对待T)。 JAXB无法处理它的原因是因为它无法知道对象是什么。
Now, if you have your own custom rules for determining the type of T (e.g. when coming from XML T is always an Integer or T is an Integer if it doesn't contain a '.' and a Double otherwise) then you can implement your own logic using an adapter which is what I've demonstrated below (I used the second rule).
现在,如果您有自己的自定义规则来确定T的类型(例如,当来自XML T时,总是一个整数,如果它不包含'。'而T是整数,那么你可以实现)你使用适配器的逻辑,这是我在下面演示的(我使用了第二条规则)。
@XmlRootElement(name="root")
public class SO {
private List<Key<?>> keys;
@XmlElementWrapper(name="Keys")
@XmlElement(name="Key")
public void setKeys(List<Key<?>> keys) {
this.keys = keys;
}
public List<Key<?>> getKeys() {
return keys;
}
@XmlType
public static class Key<T> {
private T val;
@XmlValue
@XmlJavaTypeAdapter(ToStringAdapter.class)
public void setKey(T val) {
this.val = val;
}
public String toString() {
return "Key(" + val + ")";
}
}
public static class ToStringAdapter extends XmlAdapter<String, Object> {
@Override
public Object unmarshal(String v) throws Exception {
if(v.contains(".")) {
return Double.parseDouble(v);
} else {
return Integer.parseInt(v);
}
}
@Override
public String marshal(Object v) throws Exception {
return v.toString(); //Will never be called anyway so you could also throw an exception here
}
}
private static final String XML_INT = "<root><Keys><Key>2</Key></Keys></root>";
private static final String XML_DOUBLE = "<root><Keys><Key>2.7</Key></Keys></root>";
public static void main(String [] args) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(Key.class, SO.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
SO so = (SO) unmarshaller.unmarshal(new StringReader(XML_INT));
System.out.print(so.keys);
System.out.println(" " + so.keys.get(0).val.getClass().getSimpleName());
so = (SO) unmarshaller.unmarshal(new StringReader(XML_DOUBLE));
System.out.print(so.keys);
System.out.println(" " + so.keys.get(0).val.getClass().getSimpleName());
}
}