Reputation: 1279
I have a POJO with lombok annotations which my JSON deserializes to through Jackson like so:
@Getter
@ToString
@NoArgsConstructor
@Builder
@AllArgsConstructor
@EqualsAndHashCode
@JsonIgnoreProperties(ignoreUnknown = true)
public class ResponsePOJO {
@JsonProperty("list-one")
private List<Object> list1 = Lists.newArrayList;
@JsonProperty("list-two")
private List<Object> list2 = Lists.newArrayList;
@JsonProperty("list-three")
private List<Object> list3 = Lists.newArrayList;
@JsonProperty("list-four")
private List<Object> list4 = Lists.newArrayList;
}
When jackson attempts to deserialize a response where only list-one & list-two are present, I expect the resulting POJO to have properties list3
and list4
as an empty list which is the default value, but instead they are deserialized as null
.
What is missing to insure all properties will either contain the corresponding value from the deserialized JSON, or empty list which is the default assigned value?
---Update---- This was not an issue until I upgraded from Spring 1.3.5 to 1.4.2, which also upgraded my Jackson version from 2.6.6 to 2.8.4
Upvotes: 2
Views: 9691
Reputation: 2019
I ran into a similar issue deserializing a JSON object using Jackson (com.fasterxml.jackson) and Lombok's @Builder annotation. I was able to resolve this issue by creating an empty Builder class annotated with @JsonPOJOBuilder inside the class and then adding a @JsonDeserialize with a reference to the empty class. You can find more info here
For example,
@Getter
@ToString
@NoArgsConstructor
@Builder
@AllArgsConstructor
@EqualsAndHashCode
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonDeserialize(builder = ResponsePOJO.TypeBuilder.class)
public class ResponsePOJO {
@JsonPOJOBuilder(withPrefix = "")
@JsonIgnoreProperties(ModelUtils.TYPE_INFO_PROPERTY)
public static class TypeBuilder{}//Empty builder class
//Needed just to apply annotations
//Lombok will fill it in later.
@JsonProperty("list-one")
private List<Object> list1 = Lists.newArrayList;
@JsonProperty("list-two")
private List<Object> list2 = Lists.newArrayList;
@JsonProperty("list-three")
private List<Object> list3 = Lists.newArrayList;
@JsonProperty("list-four")
private List<Object> list4 = Lists.newArrayList;
}
Upvotes: 0
Reputation: 211
Lombok's @Builder
is not a direct problem here, cause Jackson does not use it for deserialization. However, Jackson is using non-default constructor with args, if it is available, for object instantiation. Otherwise it is using non-arg constructor. When you use @AllArgsConstructor
on ResponsePOJO
, Jackson takes created constructor which looks like this:
public ResponsePOJO(List<Object> list1, List<Object> list2, List<Object> list3, List<Object> list4) {
this.list1 = list1;
this.list2 = list2;
this.list3 = list3;
this.list4 = list4;
}
And when this constructor is invoked, your default value is discarded and null
is assigned for non-existent fields in JSON.
While @Builder
requires all arguments constructor, you have few ways to fix the issue:
@Builder
and remove @AllArgsConstructor
- sometimes you may not need any of these, but it depends on your case@AllArgsConstructor
and create custom builder, which is not using all arguments constructor inside, but the no-arg one.Use @Builder
and create custom all arguments constructor similar to this one:
public ResponsePOJO(List<Object> list1, List<Object> list2, List<Object> list3, List<Object> list4) {
this.list1 = list1 == null ? Lists.newArrayList() : list1;
this.list2 = list2 == null ? Lists.newArrayList() : list2;
this.list3 = list3 == null ? Lists.newArrayList() : list3;
this.list4 = list4 == null ? Lists.newArrayList() : list4;
}
Here Jackson would still invoke all arguments constructor above, but if some field does not exist OR exists and is null (this may have downstream impact on your business logic if in some cases you may expect null values, e.g. validation against nullity), it will always assign empty list.
Upvotes: 3
Reputation: 34452
The problem is that you're also adding @Builder
. The initializer is moved to the builder, as you don't want to run the code twice.
Recent versions (1.16.16+) of lombok have the @Builder.Default that you can use to influence that behavior.
You can instruct Jackson to use the builder instead, and make your objects immutable. Builder is meant for immutable objects.
Disclosure: I am a lombok developer.
Upvotes: 1