I have these 2 XML's that I'm trying to deserialize with jackson-dataformat-xml
:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.3</version>
</dependency>
These are the 2 XML files:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<query queryname="persons">
<row>
<name>charles</name>
<userid>1</userid>
</row>
<row>
<name>arthur</name>
<userid>2</userid>
</row>
</query>
<?xml version="1.0" encoding="ISO-8859-1" ?>
<query queryname="users">
<row>
<id>1</id>
<username>charles</username>
</row>
<row>
<id>2</id>
<username>arthur</username>
</row>
</query>
The 2 XMLs display the results of 2 different queries and are differentiated by the queryname
attribute of the query
element and the row
element contains different information.
I'm trying to deserialize the 2 XMLs with a single Query class like this:
public interface IRow {
}
public class Person implements IRow {
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() + "]";
}
}
public class User extends IRow {
private String id;
private String username;
public String getId() {return id;}
public String getUserName() {return username;}
@Override
public String toString() {
return "user [id=" + getId() + ", username=" + getUserName() + "]";
}
}
public class Query {
@JacksonXmlProperty(isAttribute = true)
private String queryname;
@JacksonXmlElementWrapper(useWrapping = false)
private List<IRow> row;
public String getQueryname() {return queryname;}
public List<IRow> getRow() {return row;}
@Override
public String toString() {
return "query [queryname=" + getQueryname() + ", row=" + getRow() + "]";
}
}
public class Main {
public static void main(String[] args) throws JsonMappingException, JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
String sXml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\r\n"
+ "<query queryname=\"persons\">\r\n"
+ " <row>\r\n"
+ " <name>charles</name>\r\n"
+ " <userid>1</userid>\r\n"
+ " </row>\r\n"
+ " <row>\r\n"
+ " <name>arthur</name>\r\n"
+ " <userid>2</userid>\r\n"
+ " </row>\r\n"
+ "</query>";
Query oQuery = xmlMapper.readValue(sXml,Query.class);
System.out.println(oQuery);
}
}
I get the error The main class, rightly, throws the Exception error in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xmlmanager.jackson.IRow` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
The mistake is right, as in the Query
class I defined the row
property as a list of IRows
.
I would need something (I think annotations) that allow me to define and deserialize the row element with the class:
Person
when attribute queryname
= "persons"User
when attribute queryname
= "users"How could I do?
Thanks in advance
The Sascha's solution works even without the JsonTypeInfo
and JsonSubTypes
annotations and without using the IRow
interface:
import java.util.List;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public abstract class AbstractQuery<I> {
@JacksonXmlProperty(isAttribute = true)
private String queryname;
@JacksonXmlElementWrapper(useWrapping = false)
private List<I> row;
public String getQueryname() {return queryname;}
public List<I> getRow() {return row;}
@Override
public String toString() {
return "query [queryname=" + getQueryname() + ", row=" + getRow() + "]";
}
}
public class Person {
public static class Query extends AbstractQuery<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() + "]";
}
}
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public class User {
public static class Query extends AbstractQuery<User> {}
private String id;
@JacksonXmlProperty(localName = "username")
private String username;
public String getId() {return id;}
public String getUserName() {return username;}
@Override
public String toString() {
return "user [id=" + getId() + ", username=" + getUserName() + "]";
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class Main {
public static void main(String[] args) throws JsonMappingException, JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
String sXmlPersons = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\r\n"
+ "<query queryname=\"persons\">\r\n"
+ " <row>\r\n"
+ " <name>charles</name>\r\n"
+ " <userid>1</userid>\r\n"
+ " </row>\r\n"
+ " <row>\r\n"
+ " <name>arthur</name>\r\n"
+ " <userid>2</userid>\r\n"
+ " </row>\r\n"
+ "</query>";
Person.Query oPersonQuery = xmlMapper.readValue(sXmlPersons,Person.Query.class);
System.out.println(oPersonQuery);
String sXmlUsers = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\r\n"
+ "<query queryname=\"users\">\r\n"
+ " <row>\r\n"
+ " <id>1</id>\r\n"
+ " <username>charles</username>\r\n"
+ " </row>\r\n"
+ " <row>\r\n"
+ " <id>2</id>\r\n"
+ " <username>arthur</username>\r\n"
+ " </row>\r\n"
+ "</query>";
User.Query oUserQuery = xmlMapper.readValue(sXmlUsers,User.Query.class);
System.out.println(oUserQuery);
}
}