Jackson deserialize default values missing - java

I try to deserialize a JSON object that I receive in my API using the following code:
ObjectMapper mapper = new ObjectMapper();
ExampleDto ed = mapper.readValue(req.body(), ExampleDto.class);
My class uses Lombok to generate constructors, getters and setters, and looks like this:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ExampleDto {
private String name = "";
private List<String> values = new LinkedList<>();
}
Both properties should be optional, and use the default value specified in the class definition if they are not provided. However, if I now try to deserialize the JSON
{name: "Foo"}
the values field is null. From my understanding, and all example code I found, values should be an empty list.
Edit: Not a duplicate, as I'm using Lombok without Optionals

#AllArgsConstructor creates the following constructor
#ConstructorProperties({"name", "values"})
ExampleDto(String name, List<String> values) {
this.name = name;
this.values = values;
}
The constructor is annotated with #ConstructorProperties which means a property-based creator (argument-taking constructor or factory method) is available to instantiate values from JSON object so jackson-databind uses this constructor to instantiate an object from ExampleDto class.
When the following line executes
mapper.readValue("{\"name\": \"Foo\"}", ExampleDto.class);
because there's no value for values in the provided JSON, null is passed for the second argument when the constructor is invoked.
If you remove #AllArgsConstructor annotation jackson-databind would use setter methods to initialize the object and in this case values would not be null

Related

Jackson serialising Optional with null value for YAML

Currently I am using the YAMLFactory to configure the ObjectMapper to serialise and deserialise Pojos <=> YAML, however it writes null values in serialisation despite attempting the usual tricks in Jackson.
Annotating with #JsonInclude(JsonInclude.Include.NON_NULL) on the class level or the field level has no affect. I have also tried annotating classes with #JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) with no affect either. How do you achieve this when using the YAMLFactory?
I have found a similar question but the use case does not appear to be the same. To be clear I am trying to omit the field altogether.
Edit: Here is an example (I am also using Lombok)
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class QueueProperties extends Properties {
#JsonProperty("QueueName")
private String queueName;
#JsonProperty("RedrivePolicy")
private RedrivePolicy redrivePolicy;
public Optional<RedrivePolicy> getRedrivePolicy() {
return Optional.ofNullable(redrivePolicy);
}
}
when serialized:
Properties:
QueueName: 471416d1-3643-4d5a-a033-65f22757dcaf-chargeback-paypal-ingestion-ingest_dispute
RedrivePolicy: null
ObjectMapper configuration:
ObjectMapper mapper = new ObjectMapper(new YAMLFactory().enable(YAMLGenerator.Feature.MINIMIZE_QUOTES));
mapper.registerModule(new Jdk8Module());
getRedrivePolicy getter method always returns not-null object, even so, Optional could reference to null. In this case, you should skip empty objects. Optional with null is considered as empty and we can use JsonInclude.Include.NON_EMPTY for it. You can keep #JsonInclude(JsonInclude.Include.NON_NULL) on class level and add NON_EMPTY only for given getter:
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public Optional<Object> getRedrivePolicy() {
return Optional.ofNullable(redrivePolicy);
}

Jackson cannot construct instance with one parameter constructor

I am using Spring Boot to create a web application. One of the endpoints expect a json object having one property, i.e. studentId. I am using DTO like my other functions to capture the payload.
#PostMapping("/courses/{id}/students")
public SuccessResponse<Void> addEnrolls(#PathVariable Long id, #RequestBody StudentIdPayload payload) throws HandledException {
courseService.addEnrolls(id, payload.getStudentId());
return success(HttpStatus.OK);
}
#Data
#AllArgsConstructor
public class StudentIdPayload {
private Long studentId;
}
But when I tried to post the endpoint with json body {"studentId":1}, I got the following error :
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.bimoadityar.univms.dto.input.StudentIdPayload` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
While it works if I post using just the value 1.
How can I get it to work with the object payload?
Interestingly, when I add another property to the StudentIdPayload, such as String placeholder, it works as intended, although this solution feels hacky.
Considering https://github.com/FasterXML/jackson-databind/issues/1498, it seems that this is the intended behavior.
For my particular case, I am satisfied with adding the #JsonCreator to my constructor.
#Data
#AllArgsConstructor(onConstructor = #__(#JsonCreator))
public class StudentIdPayload {
private Long studentId;
}
By default, deserialization requires no-args constructor, so add #NoArgsConstructor:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class StudentIdPayload {
private Long studentId;
}
see also:
Annotations: using custom constructor

Unable to serialize Object to Json using Jackson

I'm trying to serialize an object in Java using Jackson, but when I'm trying to serialize it, it gives me this error:
No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer
I tried this post, but it didn't help.
Here is the class I'm trying to serialize:
public class Repository {
public String name;
#JsonIgnore // to avoid recursive calls
public ArrayList<UserRole> contributors = new ArrayList<UserRole>();
public User self;
public ArrayList<FileInfo> files;
public RepositoryType repositoryType;
public String path;
}
I also tried to create getters/setters for each field but still nothing.
Here is my serialization method:
public static String convertObjectToJson(Object object) throws IOException {
ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = objectWriter.writeValueAsString(object); //error on this line
return json;
}
Looks like your one of your classes has java.io.FileDescriptor reference.
By default, Jackson will only work with with fields that are either public, or have a public getter methods – serializing an entity that has all fields private or package private will fail
If you look at the source code of java.io.FileDescriptor you can see
there are private fields without public getters.
You should configure your objectMapper visibility to allow access to private fields also.
// For jackson 2.*
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
// For jackson lower than 2
objectMapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
I was facing problems to send objects to Thymeleaf template with ResponseEntity it was giving me exception "StackOverFlowError" while serializing and your note " #JsonIgnore // to avoid recursive calls" solved my problem. Thanks

Limiting the json fields that need to be deserialized into java object's fields

I have a Json as:
{
"a1":"a1Value",
"a2":"a2Value",
"a3":"a3Value",
"a4":"a4Value",
"a5":"a5Value"
}
Java class is:
class Response {
private String a1;
private String a2;
private String a3;
//public getters and setters
}
Now I only need 3 fields from the Json to be de-serialized into the object's 3 fields, I am using Spring 3.2.2 MVC,for REST support(rest template : getForEntity()) , and getting error during the process,How can I ignore these fields in the json ? Also, is there some way, that at the time of serialization also, I only serialize those properties of object that have non-null values at the time of serialization.
Add this annotation on class level to ignore unknown properties:
#JsonIgnoreProperties(ignoreUnknown = true)
or with ObjectMapper:
objectMapper.configure
(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
For only non-null:
#JsonInclude(Include.NON_NULL)
or for Jackson ObjectMapper:
objectMapper.setSerializationInclusion(Include.NON_NULL);

Exclude empty Arrays from Jackson ObjectMapper

I am building JSON from Java object tree using Jackson ObjectMapper. Some of my Java objects are collections and sometimes they might be empty. So if they are empty that ObjectMapper generates me: "attributes": [], and I want to exclude those kind of empty JSON arrays from my result. My current ObjectMapper config:
SerializationConfig config = objectMapper.getSerializationConfig();
config.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
config.set(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
From this post I've read that I can use:
config.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
But that is generating me an error:
Caused by: java.lang.IllegalArgumentException: Class com.mycomp.assessments.evaluation.EvaluationImpl$1 has no default constructor; can not instantiate default bean value to support 'properties=JsonSerialize.Inclusion.NON_DEFAULT' annotation.
So how should I prevent those empty arrays to appear in my result?
You should use:
config.setSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY);
for Jackson 1 or
config.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
for Jackson 2
A very good example describing :
JsonInclude.Include.NON_NULL
JsonInclude.Include.ABSENT
JsonInclude.Include.NON_EMPTY
In : https://www.logicbig.com/tutorials/misc/jackson/json-include-non-empty.html
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Employee {
private String name;
private String dept;
private String address;
private List<String> phones;
private AtomicReference<BigDecimal> salary;
.............
}
public class ExampleMain {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setName("Trish");
employee.setDept("");
employee.setAddress(null);
employee.setPhones(new ArrayList<>());
employee.setSalary(new AtomicReference<>());
ObjectMapper om = new ObjectMapper();
String jsonString = om.writeValueAsString(employee);
System.out.println(jsonString);
}
}
=====> Result :
If we don't use #JsonInclude annotation at all then output of the above example will be:
{"name":"Trish","dept":"","address":null,"phones":[],"salary":null}
If we use #JsonInclude(JsonInclude.Include.NON_NULL) on Employee class then output will be:
{"name":"Trish","dept":"","phones":[],"salary":null}
If we use #JsonInclude(JsonInclude.Include.NON_ABSENT) then output will be:
{"name":"Trish","dept":"","phones":[]}
If we use #JsonInclude(JsonInclude.Include.NON_EMPTY) :
{"name":"Trish"}
If you can modify the object to be serialized, you can also place an annotation directly on the field, for example (Jackson 2.11.2):
#JsonProperty
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private Set<String> mySet = new HashSet<>();
In this way, no further configuration of the ObjectMapper is required.

Categories