JSON: JsonMappingException while try to deserialize object with null values - java

I try to deserialize object that contains null-properties and have the JsonMappingException.
What I do:
String actual = "{\"#class\" : \"PersonResponse\"," +
" \"id\" : \"PersonResponse\"," +
" \"result\" : \"Ok\"," +
" \"message\" : \"Send new person object to the client\"," +
" \"person\" : {" +
" \"id\" : 51," +
" \"firstName\" : null}}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new StringReader(json), PersonResponse.class); //EXCEPTION!
BUT: if to throw away "firstName = null" property - all works fine!
I mean pass the next string:
String test = "{\"#class\" : \"PersonResponse\"," +
" \"id\" : \"PersonResponse\"," +
" \"result\" : \"Ok\"," +
" \"message\" : \"Send new person object to the client\"," +
" \"person\" : {" +
" \"id\" : 51}}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new StringReader(json), PersonResponse.class); //ALL WORKS FINE!
Question:
How to avoid this exception or to pledge Jackson ignore null-values during serialization?
Throws:
Message:
com.fasterxml.jackson.databind.MessageJsonException:
com.fasterxml.jackson.databind.JsonMappingException:
N/A (through reference chain: person.Create["person"]->Person["firstName"])
cause:
com.fasterxml.jackson.databind.MessageJsonException:
com.fasterxml.jackson.databind.JsonMappingException:
N/A (through reference chain: prson.Create["person"]->Person["firstName"])
cause: java.lang.NullPointerException

Sometimes this problem occurs when accidentally using a primitive type as return type of the getter of a non-primitive field:
public class Item
{
private Float value;
public float getValue()
{
return value;
}
public void setValue(Float value)
{
this.value = value;
}
}
Notice the "float" instead of "Float" for the getValue()-method, this can lead to a Null Pointer Exception, even when you have added
objectMapper.setSerializationInclusion(Include.NON_NULL);

If you don't want to serialize null values, you can use the following setting (during serialization):
objectMapper.setSerializationInclusion(Include.NON_NULL);
Hope this solves your problem.
But the NullPointerException you get during deserialization seems suspicious to me (Jackson should ideally be able to handle null values in the serialized output). Could you post the code corresponding to the PersonResponse class?

I also faced the same issue.
I just included a default constructor in the model class along with the other constructor with parameters.
It worked.
package objmodel;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class CarModel {
private String company;
private String model;
private String color;
private String power;
public CarModel() {
}
public CarModel(String company, String model, String color, String power) {
this.company = company;
this.model = model;
this.color = color;
this.power = power;
}
#JsonDeserialize
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
#JsonDeserialize
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#JsonDeserialize
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
#JsonDeserialize
public String getPower() {
return power;
}
public void setPower(String power) {
this.power = power;
}
}

Add JsonProperty annotation to your attribute in TO class, as below
#JsonProperty
private String id;

Related

Deserialize json array values to specific Java class fields

I have the following Json
{
"coreId" : "1",
"name" : "name",
"additionalValueList" : [
{
"columnName" : "allow_duplicate",
"rowId" : "10",
"value" : "1"
},
{
"columnName" : "include_in_display",
"rowId" : "11",
"value" : "0"
},
...e.t.c
]
},
...e.t.c
and Java class
class DTO {
#JsonProperty("coreId")
private Integer id;
private String name;
private Boolean allowDuplicate;
private Boolean includeInDisplay;
}
How I can easily map values from 'additionalValueList' to corresponding java fields.For example Json value from field 'columnName' - 'allow_duplicate' = DTO.allowDuplicate.
Actually I know how to do it with custom deserializers with #JsonDeserialize annotation and smth like this.Bu I have 40+ DTO and it is not a good idea to create own deserializer for each filed. I am looking for solution to have for example 1 deserializer(since values structure in 'additionalValueList' are the same for all entities) and to pass parameter(field name that I want to map to that field) to custom deserializer that will find in 'additionalValueList' entity with 'column Name' = parameter(that I passed from annotation) and return 'value'.
Example
class DTO {
#JsonProperty("coreId")
private Integer id;
private String name;
#JsonDeserialize(using = MyCustDeser.class,param = allow_duplicate)
private Boolean allowDuplicate;
#JsonDeserialize(using = MyCustDeser.class,param = include_in_display)
private Boolean includeInDisplay;
}
It will be a good solution but maybe not easy to achieve.However I will be very grateful for all your advices.Thank you.
Create a Converter class, then specify it on the DTO class.
The following code uses public fields for the simplicity of the example.
/**
* Intermediate object used for deserializing FooDto from JSON.
*/
public final class FooJson {
/**
* Converter used when deserializing FooDto from JSON.
*/
public static final class ToDtoConverter extends StdConverter<FooJson, FooDto> {
#Override
public FooDto convert(FooJson json) {
FooDto dto = new FooDto();
dto.name = json.name;
dto.id = json.coreId;
dto.allowDuplicate = lookupBoolean(json, "allow_duplicate");
dto.includeInDisplay = lookupBoolean(json, "include_in_display");
return dto;
}
private static Boolean lookupBoolean(FooJson json, String columnName) {
String value = lookup(json, columnName);
return (value == null ? null : (Boolean) ! value.equals("0"));
}
private static String lookup(FooJson json, String columnName) {
if (json.additionalValueList != null)
for (FooJson.Additional additional : json.additionalValueList)
if (columnName.equals(additional.columnName))
return additional.value;
return null;
}
}
public static final class Additional {
public String columnName;
public String rowId;
public String value;
}
public Integer coreId;
public String name;
public List<Additional> additionalValueList;
}
You now simply annotate the DTO to use it:
#JsonDeserialize(converter = FooJson.ToDtoConverter.class)
public final class FooDto {
public Integer id;
public String name;
public Boolean allowDuplicate;
public Boolean includeInDisplay;
#Override
public String toString() {
return "FooDto[id=" + this.id +
", name=" + this.name +
", allowDuplicate=" + this.allowDuplicate +
", includeInDisplay=" + this.includeInDisplay + "]";
}
}
Test
ObjectMapper mapper = new ObjectMapper();
FooDto foo = mapper.readValue(new File("test.json"), FooDto.class);
System.out.println(foo);
Output
FooDto[id=1, name=name, allowDuplicate=true, includeInDisplay=false]

How to convert JSON field name to Java bean class property with Jackson

I have access to a RESTful API which returns JSON Strings, such as the following:
{
"Container1": {
"active": true
},
"Container2": {
"active": false
},
}
The problem is that the RESTful API is a bit maldesigned. The field name contains the data already. With the Jackson library it is not possible to deserialize the field name to a property name of the corresponding Java bean class. I assume, this isn't intended by the JSON specification neither. The above JSON string needs to be deserialized to an instance of the following class:
public class Container {
private Boolean active;
private String name;
}
I end up with UnrecognizedPropertyException for the field Container1.
I thought to configure to ignore unknown properties and to provide a JsonDeserializer for that property like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Container {
private Boolean active;
private String name;
#JsonDeserialize(using = FieldNameToPropertyDeserializer.class)
public void setName(String name) {
this.name = name;
}
}
and the FieldNameToPropertyDeserializer:
public class FieldNameToPropertyDeserializer extends StdDeserializer<String> {
public FieldNameToPropertyDeserializer() {
super(String.class);
}
#Override
public String deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
return parser.getCurrentName();
}
}
The invocation of the deserialization is achieved as follows:
String jsonString = response.readEntity(String.class);
ObjectMapper objectMapper = new ObjectMapper();
ObjectReader readerFor = objectMapper.readerFor(Container.class);
MappingIterator<Container> mappingIterator = readerFor.readValues(jsonString);
while (mappingIterator.hasNext()) {
Container container = (Container) mappingIterator.next();
containers.add(container);
}
But I only receive empty objects (properties set to null) because the parsing of the properties is skipped since I set #JsonIgnoreProperties(ignoreUnknown = true).
Is this possible at all? Or should I implement something like a post-processing afterwards?
How about this. Create a class ContainerActive like this
public class ContainerActive {
private boolean active;
// constructors, setters, getters
}
And you could just do
Map<String, ContainerActive> map = mapper.readValue(jsonString, new TypeReference<Map<String, ContainerActive>>() {});
With this you will have "Container1", "Container2" as the keys and ContainerActive Object as values which has active field.
Just a quick solution, if the object is such that, that all of it object is a container object you can receive the JSON inside and JSONObject you may use below code
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestSO {
public static void main(String[] args) throws JsonParseException, JsonMappingException, JSONException, IOException {
String jsonString = "{\r\n" +
" \"Container1\": {\r\n" +
" \"active\": true\r\n" +
" },\r\n" +
" \"Container2\": {\r\n" +
" \"active\": false\r\n" +
" },\r\n" +
"}";
JSONObject jsonObject = new JSONObject(jsonString);
ObjectMapper mapper = new ObjectMapper();
for (String key : jsonObject.keySet()) {
Container container = mapper.readValue(jsonObject.get(key).toString(), Container.class);
System.out.println(container);
}
}
static class Container{
private String name;
private Boolean active;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
#Override
public String toString() {
return "Container [name=" + name + ", active=" + active + "]";
}
}
}

ACCEPT_EMPTY_STRING_AS_NULL_OBJECT with List field

with Jackson, I'm trying to properly deserialize a JSON which contains empty string as no value values. Herein an example:
{
"code" : "REQ500",
"description" : "Problem with Service",
"params" : ""
}
and the bean I would get:
public final class MyError {
#JsonProperty("code")
private String code;
#JsonProperty("description")
private String description;
#JsonProperty("params")
private List<String> params;
// [CUT]
public void setParams(List<String> params) {
this.params = params;
}
}
Using ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, I've create an object mapper with
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
.registerModule(new SimpleModule().addDeserializer(LocalDate.class, new DateDeserializer()));
expecting to have params = null, but deserialization doesn't work. Herein the error I got:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
deserialize instance of `java.util.ArrayList` out of VALUE_STRING token
at [Source: (String)"{
"code" : "REQ500",
"description" : "Problem with Service",
"params" : ""
}"; line: 4, column: 14] (through reference chain: solutions.infinitec.fabrick.models.common.MyError["params"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
Any hints on how to solve this issue? Am I wrong somewhere?
Thank you in advance for suggestions.
I tried it using ACCEPT_SINGLE_VALUE_AS_ARRAY and it works as expected.
ObjectMapper om = new ObjectMapper();
om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
MyJson asString = om.readValue("{\n"
+ " \"code\" : \"REQ500\",\n"
+ " \"description\" : \"Problem with Service\",\n"
+ " \"params\" : \"\"\n"
+ "}", MyError.class);
MyJson asArray = om.readValue("{\n"
+ " \"code\" : \"REQ500\",\n"
+ " \"description\" : \"Problem with Service\",\n"
+ " \"params\" : [\"\"]\n"
+ "}", MyError.class);
The only thing you have to handle is the response containing just a single element in params which is empty.
Another way could be this definition of MyError:
public class MyError {
#JsonProperty("code")
private String code;
#JsonProperty("description")
private String description;
private List<String> params;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<String> getParams() {
return params;
}
#JsonProperty("params")
private void setParams(JsonNode params) {
if (params.isArray()) {
this.params = new ArrayList<>();
for(JsonNode child : params) {
this.params.add(child.asText());
}
}
}
}
I'd try using
OBJECT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
As shown in "Cannot deserialize instance of java.util.ArrayList"
Just implement a setter method which accept string:
public void setParams(String params) {
this.params = null;
}
if want to accept array:
public void setParams(String[] params) {
this.params = Arrays.asList(params);
}

Jackson calls setter but field of nested object remains null

There is a class with another static one inside. Jackson calls setters while deserializing json. But some setters doesn't work as expected.
Classes def:
#Entity
#JsonIgnoreProperties(ignoreUnknown=true)
#JsonPropertyOrder({ "name", "imageLink", "weeklyGross", "weeklyChange" })
public class WeeklyBoxOffice {
#Embeddable
public static class WeeklyBoxOfficeID implements Serializable {
public String country;
public String name;
public WeeklyBoxOfficeID() {
}
}
public WeeklyBoxOffice() {
this.id = new WeeklyBoxOfficeID();
}
#EmbeddedId
#JsonUnwrapped
WeeklyBoxOfficeID id;
private long weeklyGross;
private double weeklyChange;
// "weeklyGross":"$294,710"
public void setWeeklyGross(String weeklyGross) throws ParseException {
System.out.println("setWeeklyGross called, weeklyGross = " + weeklyGross);
this.weeklyGross = (long) NumberFormat.getCurrencyInstance(Locale.US)
.parse(weeklyGross);
}
// "weeklyChange": "-57.1%"
public void setWeeklyChange(String weeklyChange) {
this.weeklyChange = weeklyChange.equals("-") ? 0 : Double.parseDouble(weeklyChange.replace('%', ' '));
}
public void setCountry(String country) {
this.id.country = country;
}
// #JsonProperty("name")
public void setName(String name) {
System.out.println("Set name caled, name = " + name);
this.id.name = name;
}
public void setMovie(Movie movie) {
this.movie = movie;
}
public static void main(String[] args) throws IOException {
String json = "{\n" +
" \"TrendingMovies\": [\n" +
" {\n" +
" \"name\": \"The Dark Tower\",\n" +
" \"weeklyGross\": \"$461,377\",\n" +
" \"weeklyChange\": \"5.0\"\n" +
" }\n" +
" ]\n" +
"}\n";
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
List<WeeklyBoxOffice> list = mapper.readValue(mapper.readTree(json).get("TrendingMovies").toString(), new TypeReference<List<WeeklyBoxOffice>>() {});
list.forEach(System.out::println);
}
Output:
Set name caled, name = The Dark Tower
setWeeklyGross called, weeklyGross = $461,377
WeeklyBoxOffice{ id=WeeklyBoxOfficeID{country='null', name='null'},
weeklyGross=461377, weeklyChange=5.0 }
Why call of setName has no effect while setWeeklyGross and setWeeklyChange does and the fields of nested object remains null?
The issue is the #JsonUnwrapped and it will work fine if your remove it. If you follow your code with a debugger, you'll see that Jackson (in this order):
Calls WeeklyBoxOffice constructor that sets WeeklyBoxOfficeID id to an empty object (all null).
Calls setters for each JSON field by matching names. setName is called and name is set inside WeeklyBoxOfficeID id.
Handles any fields annotated as unwrapped. It can't find a name field in JSON as it was consumed before by getName. Therefore it now sets it to null.
The code will also work if you keep #JsonUnwrapped but remove the setCountry and setName setters which is presumably simpler. Basically use one or the other.

How to change the json response fields to user defined fields in java?

I have the following json response:
{
"Labels": {
"com.prop.vendor": "Acme",
"com.example.license": "GPL",
"com.example.version": "1.0"
}
}
Now I want to process these values in my java class. The problem I have is for fields like com.example.version, pojo is being generated using the same as data types. Like,
private String com.example.version
which gives me compile-time errors.
So, I want to create a field "String version" which points to "com.example.version"
And I have tried using #JsonProperty and #JsonSetter.
Labels.java
public class Labels
{
#JsonProperty("com.example.version")
private String version;
#JsonProperty("com.example.vendor")
private String vendor;
#JsonProperty("com.example.license")
private String license;
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getVendor()
{
return vendor;
}
public void setVendor(String vendor)
{
this.vendor = vendor;
}
public String getLicense()
{
return license;
}
public void setLicense(String license)
{
this.license = license;
}
#Override
public String toString()
{
return "ClassPojo [com.example.version = " + version + ", com.example.vendor = " + vendor + ", com.example.license = "
+ license + "]";
}
}
But everytime I am getting this error:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Labels" (class com.csc.agility.adapters.service.docker.pojo.Labels), not marked as ignorable (3 known properties: "com.example.license", "com.example.vendor", "com.example.version"])
at [Source: {
"Labels": {
"com.example.vendor": "Acme",
"com.example.license": "GPL",
"com.example.version": "1.0"
}
}; line: 2, column: 12] (through reference chain: com.csc.agility.adapters.service.docker.pojo.Labels["Labels"])
How I am using it:
ObjectMapper objMap = new ObjectMapper();
String jsonInString =
"{\n\"Labels\": {\n \"com.example.vendor\": \"Acme\",\n \"com.example.license\": \"GPL\",\n \"com.example.version\": \"1.0\"\n }\n}";
Label label = objMap.readValue(jsonInString, Label.class);
String licence = label.getLicense();
Can anyone point me to the correct json annotation to use or any other way to achieve it?
You should configure your mapper with
mapper.configure(Feature.UNWRAP_ROOT_VALUE, true); (for version 1.9)
or
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); (for version 2 and above).
I have tried your json with above properties and everything works fine:
String data = "{\"Labels\": {\"com.example.vendor\": \"Acme\",\"com.example.license\": \"GPL\",\"com.example.version\": \"1.0\"}}";
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
Labels labels = mapper.readValue(data, Labels.class);
System.out.println(labels);
Note: In Json String, I have changed com.prop.vendor to com.example.vendor to make it workable.
Have one more pojo like below and use this object instead of Labels to serialize/deserialize -
public class Response {
#JsonProperty("Labels")
private Labels labels;
//setter
//getter
}
In your code use this -
Response response = objMap.readValue(jsonInString, Response.class);
String licence = response.getLabels().getLicense();

Categories

Resources