Polymorphic deserialization Jackson issue - java

Java :
/**
*/
#JsonTypeInfo(include=As.PROPERTY, property="_type", use=Id.NAME)
#JsonSubTypes({
#JsonSubTypes.Type(value=Dog.class, name="dog"),
#JsonSubTypes.Type(value=Cat.class, name="cat"),
})
#ResourceName("animal");
public interface AnimalDto {
/**
* Stop gap property to deal with jackson not serializing type information
* when using {#link JsonTypeInfo}
*
* #return A string name of the type of Animal
*/
#JsonProperty("_type")
}
public abstract class WildAnimal implements AnimalDto {
private final String type;
#Override
public String getType(){
return this.type;
}
}
#JsonTypeName("dog")
public class Dog implements WildAnimal {
}
#JsonTypeName("cat")
public class Cat implements WildAnimal {
}
JSON :
{
"animal": {
"_type":"dog",
"id": "1",
}
}
When I am trying to deserialize above json response to java, jackson throws the below error. Can someone please help me resolve this issue.
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property '_type' that is to contain type id (for class Animal)
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#4b10fe3f; line: 1, column: 111]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property '_type' that is to contain type id (for class Animal)
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#4b10fe3f; line: 1, column: 111]
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:228)
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.read(MappingJackson2HttpMessageConverter.java:220)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:795)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:779)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:559)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:512)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:454)

You can see this post I think you'll find what you want :
Jackson JSON Polymorphism
PS : be carreful, you sometime have "_type" and sometime "type".

Related

Unrecognized field during deserialization despite #SerializedName annotation

I am fetching some data via a REST service, but I am getting this error when deserializing the response :
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "ResultSet Output" (class com.HolderCollectionWrapper), not marked as ignorable (one known property: "holders"]) at [Source: java.io.ByteArrayInputStream#74efa7bd; line: 1, column: 22] (through reference chain: com.HolderCollectionWrapper["ResultSet Output"])
This is my code :
response = restTemplate.exchange(requestUrl, HttpMethod.GET, request, HolderCollectionWrapper.class);
public class HolderCollectionWrapper {
#SerializedName("ResultSet Output")
private List<Holder> holders;
public List<Holder> getHolders() {
return holders;
}
public void setHolders(List<Holder> holders) {
this.holders = holders;
}
}
This is the JSON I am getting :
{
"ResultSet Output": [
{...}, {...}, {...}
]
}
Despite the #SerializedName("ResultSet Output"), it's not working, why ?
#SerializedName is a gson annotation and you are using jackson library for serialization.
The jackson annotation for field name is #JsonProperty
Try:
#JsonProperty("ResultSet Output")
private List<Holder> holders;
This happens because the SerializedName("ResultSet Output") gson annotation indicates that the holders will be serialized with the ResultSet Output name like the json example you post; to deserialize it with jackson you have to use the JsonProperty annotation, specifying the ResultSet Output name applied on the setter to avoid possible conflicts with the gson library used for serialization:
public class HolderCollectionWrapper {
#SerializedName("ResultSet Output")
private List<Holder> holders;
public List<Holder> getHolders() {
return holders;
}
#JsonProperty("ResultSet Output")
public void setHolders(List<Holder> holders) {
this.holders = holders;
}
}

Jackson ObjectMapper deserialize an object which contains an array of objects

I serialize this kind of object:
public class MyObject implements Serializable {
private String type;
...
private String[] target;
//getters and setters
}
But when I try to deserialize MyObject I get an error because of the target array.
java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: java.util.ArrayList[0]->MyObject["target"])
...
How can I deserialize an array ?
I finally found the problem. I did not see I had 2 setters in the class. Jackson was probably using the wrong one.
I just had to put the annotation #JsonSetter("target") above the setter which accept an array to tell Jackson to use the good one.
public void setTarget(String target) {
this.target = new String[]{target};
}
#JsonSetter("target")
public void setTarget(String[] target) {
this.target = target;
}

JSON Jackson with Generic Constraint

I'm using JSON Jackson on SpringBoot context.
I have a problem with Jackson deserialization with Generic type constraint.
This code work fine :
public abstract class AbstractSyncableWriteAndReadResource<T, K> extends AbstractSyncableReadResource<T, K> {
...
#Timed
#RequestMapping(method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional
public ResponseEntity<List<SynQueuDTO<T>>> syncPushPut(#RequestBody List<SynQueuDTO<T>> entities) {
...
}
}
But this code don't work :
This code work fine :
public abstract class AbstractSyncableWriteAndReadResource<T extends EntitySyncableWrite, K> extends AbstractSyncableReadResource<T, K> {
...
#Timed
#RequestMapping(method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#Transactional
public ResponseEntity<List<SynQueuDTO<T>>> syncPushPut(#RequestBody List<SynQueuDTO<T>> entities) {
...
}
}
There is only one difference : add Java interface on Generic for constraint Class's type.
Exception :
Type definition error: [simple type, class xxx.xxx.xxx.EntitySyncableWrite]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xxx.xxx.xxx.EntitySyncableWrite` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 5, column: 26]
I try a lot of configurations to force Jackson take Class instead of Interface informations but without success :
#JsonTypeInfo(use= JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.WRAPPER_OBJECT)
or #JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") (ko)
Do you have some idea?
Thanks
[UPDATE]
An another use case that work very well :
public abstract class AbstractSyncableWriteAndReadResource<T extends EntitySyncableWrite, K> ... {
public ResponseEntity<List<SyncQueuDTO<T>>> syncPushPut( #RequestBody SyncQueuDTO<T> data) {
...
}
}
But don't work when add RequestBody as List :
public abstract class AbstractSyncableWriteAndReadResource<T extends EntitySyncableWrite, K> ... {
public ResponseEntity<List<SyncQueuDTO<T>>> syncPushPut( #RequestBody List<SyncQueuDTO<T>> data) {
...
}
}
Exception :
Type definition error: [simple type, class xx.xx.xx.EntitySyncableWrite]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `xx.xx.xx.EntitySyncableWrite` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 5, column: 26] (through reference chain: java.util.ArrayList[0]->xx.xx.xx.SyncQueuDTO[\"serverVersion\"])
With Map JSON array of objects to #RequestBody List<T> using jackson inspiration I solved this Jackson/JVM limitation as :
public abstract class AbstractSyncableWriteAndReadResource<T extends EntitySyncableWrite, K> extends AbstractSyncableReadResource<T, K> {
...
public ResponseEntity<List<SyncQueuDTO<T>>> syncPushPut(#RequestBody SyncQueuDTOList<T> queuList) {
....
}
}
and ArrayList implementation :
public class SyncQueuDTOList<T extends EntitySyncableWrite> extends ArrayList<SyncQueuDTO<T>> {}

User-understandable JsonMappingException

I am using Jackson to read a user-provided JSON file, mapping it on an object. In case of error in the JSON structure, I get a JsonMappingException that is not readable by the user. For example:
my.package.shadow.jackson.databind.JsonMappingException: Class java.util.ArrayList not subtype of [map type; class java.util.Map, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]] (through reference chain: my.package.configuration.Configuration["conf"]->java.util.HashMap["foo"])
at my.package.shadow.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:391)
...
Caused by: java.lang.IllegalArgumentException: Class java.util.ArrayList not subtype of [map type; class java.util.Map, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
at my.package.shadow.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:357)
...
Is there a way to get a more readable error message, or the line and column in the JSON source that corresponds to the exception?
To clarify, the user writes this
{
"conf": {
"foo": ["bar"]
}
}
but I expect
{
"conf": {
"foo": {
"bar" : ["a", "b", "c"]
}
}
}
A snippet of the code:
public class ReadConfiguration {
public static Configuration readConfiguration(File file) {
ObjectMapper mapper = new ObjectMapper(new JsonFactory());
Configuration conf = mapper.readValue(file, Configuration.class);
return conf
}
}

Error in JSON list deserialize

I'm developing a GWT web application with a RESTful web service. The web service's results are deserializing to POJO with Jackson 1.8. It's work fine with simple fields. However, it fails when it try to deserialize a list of POJO. This is the POJO with list to deserialize:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DatosIndicadoresSVclaveDTO implements Serializable {
...
#XmlAttribute
#JsonDeserialize(contentAs = IdeologicoVOXDTO.class)
public List<IdeologicoVOXDTO> ideologicoVox;
...
//getter/setters
}
And this is the POJO that contains the list
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class IdeologicoVOXDTO implements Serializable {
#XmlAttribute
private Integer numeroPalabra;
#XmlAttribute
private String palabra;
#XmlAttribute
private Integer categoria;
...
//getter/setters
}
The JSON have this structure:
{datosIndicadoresSVclave: {
...
"ideologicoVox":[
{
"categoria":"1",
"numeroPalabra":"1",
"palabra":"abandonado",
...
},
{
"categoria":"2",
"numeroPalabra":"3",
"palabra":"hambre",
...
}
],
...
}
}
When it's running, the web service's results works fine, but the deserialize print this error:
SEVERE: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: java.io.StringReader#10b61ad; line: 1, column: 580] (through reference chain: org.ull.etsii.client.dto.DatosIndicadoresSVclaveDTO["ideologicoVox"])
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:212)
Any idea?? Thanks!!
I had faced some similar kind of problem and tried the following way, and it worked for me.
Create a new class which contains property
~ public List ideologicoVox ~
And use this class reference as property to the main class i.e ~ DatosIndicadoresSVclaveDTO
I've solved!!
The problem is the size list is variable, and it fails if it has one element. The Jackson's version is 1.7, that it can't accept arrays single value. My solution is GSON with a custom register type, and I've used the Joshi's adviced. It works great!! Thanks!!

Categories