2

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

5
  • Does Lists.newArrayList Creates new instance of array list ? Commented Dec 17, 2017 at 22:08
  • Correct. Lists.newArrayList() is a method by the google guava library which does as it sounds. I have also tried removing the NoArgsConstructor by lombok & defining my own which initializes all fields to empty lists and it still does not work Commented Dec 17, 2017 at 22:14
  • Do you have guava Jackson binding module in class path? Commented Dec 19, 2017 at 20:11
  • I also wonder what is the list type you are getting when you deserialize the list1 and list2 nodes Commented Dec 19, 2017 at 20:17
  • have you got answer ? if yes mark correct answer or add your answer Commented Jan 20, 2018 at 9:40

3 Answers 3

3

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:

  1. Not use @Builder and remove @AllArgsConstructor - sometimes you may not need any of these, but it depends on your case
  2. Remove @AllArgsConstructor and create custom builder, which is not using all arguments constructor inside, but the no-arg one.
  3. 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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

I tried adding @Builder.Default, defined an explicit Builder w/ default values as mentioned in reinhard.codes/2016/07/13/… and even removed @Builder entirely and the problem still persists.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.