Jackson ObjectMapper deserialize an object which contains an array of objects - java

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;
}

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;
}
}

Default value in POJO field incase of data type mismatch during JSON deserialization

When we deserialize JSON into POJO, JACKSON throws a Mismatch Input exception in case of data type mismatch, which is expected.
So my question is, "Is there any feature flag available in Jackson or Gson, to set the default value for mismatched field during deserialization?"
I understand that we can implement the custom deserializer for such fields, where random input value can be expected.
But is this already supported using some flag in any serialization deserialization java library?
Sample Code:
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.io.IOException;
public class JsonSample {
#Data
static class Address {
String houseAddress;
}
#Data
static class InnerJsonClass {
Integer integerField;
Address address;
}
public static void main(String[] args) {
String jsonInput = "{\"integerField\": 1,\"address\":\"hello\"}";
try {
ObjectMapper objectMapper = new ObjectMapper();
InnerJsonClass object = objectMapper.readValue(jsonInput, InnerJsonClass.class);
System.out.println(object.toString());
} catch (IOException e) {
e.printStackTrace();
}
}}
Exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.paytm.logistics.JsonSample$Address (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('hello')
at [Source: (String)"{"integerField": 1,"address":"hello"}"; line: 1, column: 30] (through reference chain: com.paytm.logistics.JsonSample$InnerJsonClass["address"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1031)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1366)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
at com.paytm.logistics.JsonSample.main(JsonSample.java:25)
Address is a object, not a String.
String jsonInput = "{\"integerField\": 1,\"address\":{\"houseAddress\": \"hello\"}}";
Try this
Your jsoninput is not in correct format it should be like below:
objectMapper will only convert value in specified object when json and pojo declaration mapping are same.
String jsonInput = "{\"integerField\": 1,\"address\":{\"houseAddress\": \"hello\"}}";
or if you don't want to change string json then you have to use code like below
#Data
static class InnerJsonClass {
Integer integerField;
String address;
}

JsonMappingException: Direct self-reference leading to cycle (through reference chain: MyClass["underlyingValue"])

I have a POJO class extending net.sf.gilead.pojo.gwt.LightEntity. I am not able to serialize the POJO object to JSON string using com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(obj). I am getting this error
com.fasterxml.jackson.databind.JsonMappingException:
Direct self-reference leading to cycle
(through reference chain: com.example.MyClass["underlyingValue"])
Noticed that the LightEntity class has this method:
public Object getUnderlyingValue() {
return this;
}
How about to try to override this method in your MyClass class and add the #JsonIgnore annotation?
Example:
public class MyClass extends LightEntity {
#JsonIgnore
#Override
public Object getUnderlyingValue() {
return super.getUnderlyingValue();
}
}

UnrecognizedPropertyException from jackson when reading data produced by jersey-json

I have this JAXB-annotated class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class SomeClass {
#XmlAttribute
public String value;
}
Based on this class, a JSON document was produced using jersey-json (via com.sun.jersey.api.json.JSONJAXBContext):
{
"#value":"someValue"
}
Note that jersey decided to use #value as tag name (and not simply value), probably in order to honour the #XmlAttribute annotation.
Now the task is to read this json document using jackson-json and produce an instance of the JAXB annotated class:
ObjectMapper mapper = new ObjectMapper();
JaxbAnnotationIntrospector introspector = new JaxbAnnotationIntrospector(mapper.getTypeFactory());
DeserializationConfig deserConfig = mapper.getDeserializationConfig().with(introspector);
mapper.setConfig(deserConfig);
SerializationConfig serConfig = mapper.getSerializationConfig().with(introspector);
mapper.setConfig(serConfig);
mapper.readValue(jsonFile, SomeClass.class);
This fails with the following exception:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "#value" (class test.jersey.vs.jackson.SomeClass), not marked as ignorable (one known property: "value"])
at [Source: C:\Users\AppData\Local\Temp\junit5080915042527904512\json.txt; line: 1, column: 12] (through reference chain: test.jersey.vs.jackson.SomeClass["#value"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:62)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:834)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1093)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1477)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1455)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:282)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2740)
My question now is whether there is a way to tell jackson to associate the #value tag from the JSON document with the value field of the class.
I tried specifying a PropertyNamingStrategy, but that was never called during deserialization.
PropertyNamingStrategy pns = new PropertyNamingStrategy() {
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
if (field.hasAnnotation(XmlAttribute.class)) {
if (defaultName.startsWith("#")) {
return defaultName.substring(1);
}
}
return super.nameForField(config, field, defaultName);
}
};
DeserializationConfig deserConfig = mapper.getDeserializationConfig().with(introspector).with(pns);
mapper.setConfig(deserConfig);

Polymorphic deserialization Jackson issue

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".

Categories