I'm trying to deserialize some XML using generics and I get an unexpected result. Here is a simplified example of the code I'm using:
Maven dependecy
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.3</version>
</dependency>
Xml to deserialize
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xml_result>
<row>
<name>charles</name>
<userid>1</userid>
</row>
<row>
<name>arthur</name>
<userid>2</userid>
</row>
</xml_result>
The row
element can contain different information. The example contains people information
Generic class
import java.util.List;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public class XmlGeneric<T> {
@JacksonXmlProperty(localName = "row")
@JacksonXmlElementWrapper(useWrapping = false)
private List<T> rows;
public List<T> getRow() {return rows;};
@Override
public String toString() {
return "XmlGeneric [rows=" + rows + "]";
}
}
POJO Person
public class Person {
private String name;
private String userid;
public String getName() {return name;}
public String getUserid() {return userid;}
@Override
public String toString() {
return "person [name=" + getName() + ", userid=" + getUserid() + "]";
}
}
If I use the following code, the deserialization is successful
XmlMapper xmlMapper = new XmlMapper();
XmlGeneric<Person> xmlPerson = xmlMapper.readValue(xmlPersonData,new TypeReference<XmlGeneric<Person>>() {});
System.out.println(xmlPerson);
// Result: XmlGeneric [rows=[person [name=charles, userid=1], person [name=arthur, userid=2]]]
For project needs, however, I need the deserialization to be done in another class. I tried this, but the result is not what I wanted
public class MyXmlManager<T> {
final static XmlMapper oXmlMapper = new XmlMapper();
public XmlGeneric<T> xmlDeserialization(String sXmlData) throws JsonMappingException, JsonProcessingException {
return oXmlMapper.readValue(sXmlData,new TypeReference<XmlGeneric<T>>() {});
}
}
MyXmlManager<Person> myXmlManager = new MyXmlManager<Person>();
XmlGeneric<Person> xmlPerson = myXmlManager.xmlDeserialization(xmlPersonData);
System.out.println(xmlPerson);
// Result: XmlGeneric [rows=[{name=charles, userid=1}, {name=arthur, userid=2}]]
As you can see, it does not deserialize the row
elements in the Person
POJO, but returns a LinkedHashMap
with its value corresponding to a JSON format ({name=charles, userid=1}
).
In fact, trying to cast, I get the error java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to Person
.
I know that I could use the com.fasterxml.jackson.databind.ObjectMapper
object to deserialize the JSON in the Person POJO, but I would like to understand why using the MyXmlManager<T>
class the deserialization doesn't work correctly.
It is as if the generic type passed to instantiate the MyXmlManager
was not correctly passed to the XmlMapper.readValue
method.
Thanks in advance
I found the solution, I report it below, maybe it can help someone:
Class<T>
property to the MyXmlManager<T>
classClass<T>
to instantiate the added propertyXmlMapper.readValue
method using the mapper.getTypeFactory().constructParametricType
method.This is the modified code:
public class MyXmlManager<T> {
final static XmlMapper oXmlMapper = new XmlMapper();
private Class<T> clazz;
public MyXmlManager(Class<T> clazz) {
this.clazz = clazz;
}
public XmlGeneric<T> xmlDeserialization(String sXmlData) throws JsonMappingException, JsonProcessingException, InstantiationException, IllegalAccessException {
return oXmlMapper.readValue(sXmlData, oXmlMapper.getTypeFactory().constructParametricType(XmlGeneric.class, clazz));
}
}
Test
MyXmlManager<Person> myXmlManager = new MyXmlManager<Person>(Person.class);
XmlGeneric<Person> xmlPerson = myXmlManager.xmlDeserialization(xmlPersonData);
System.out.println(xmlPerson);
// Result: XmlGeneric [rows=[person [name=charles, userid=1], person [name=arthur, userid=2]]