Builders and JSON – A Lombok and Jackson Love Story

I’m a big Lombok fan. I have been using it in every project since I discovered it. One of my favorite usages is the auto generation of the builder class enabling the builder pattern. In my opinion, builders make code more readable and fluent as well as simplifying the simplifying initialization of scenarios for testeded class. However, when it came to classes that should be deserialized from JSON string to POJOs, I had to reluctantly stray from the builder pattern, as I believed Jackson needed setters or constructors to initialize objects. To my recent surprise, it is possible to retain your beloved-friendly-neighborhood builders AND deserialize your JSONs!

This is achievable using two annotations in available by pulling in jackson-databind dependency: @JsonDeserialize and @JsonPojoBuilder (introduced in version 2.0).

Following is an example of a simple class with default builder.

@JsonDeserialize(builder = SomeClass.SomeClassBuilder.class)
@Builder
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class SomeClass {
    private String myField1;
    private String myField2;

    @JsonPOJOBuilder(withPrefix = "")
    public static class SomeClassBuilder { }

}

Comments:

  • I have added private constructors as to illustrate that the object creation did not rely on constructors. This is of course not necessary and depends on your needs.
  • The@JsonPOJOBuilder(withPrefix = “”) annotation is necessary as it tells Jackson that builder methods in the builder class are simply the names of the properties of the to-be-built class. By default Jackson will search for method “withX” (where X is the property name) in the builder class,  however Lombok builds methods with names identical to the properties in the class without any prefix, hence changing the method prefix from “with” to “”. This annotation can be redundant if you annotate the @Builder method with setterPrefix = “with” to be in line with Jackson’s default.
  • The @JsonDeserialize(builder = BuilderClass.BuilderClassBuilder.class) annotation is necessary to tell Jackson to use a builder to instantiate the class.

But what happens if my class is extending another class? This has a solution too!

Our parent and child classes will look as follows:

@JsonDeserialize(builder = SomeClass.SomeClassBuilder.class)
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class SomeClass {

    private String myField1;
    private String myField2;

    @JsonPOJOBuilder(withPrefix = "")
    public static class SomeClassBuilder { }

}
@JsonDeserialize(builder = ExtendingSomeClass.ExtendingSomeClassBuilder.class)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExtendingSomeClass extends SomeClass {

    private String myField3;

    @Builder
    public ExtendingSomeClass(String myField1, String myField2, String myField3) {
        super(myField1, myField2);
        this.myField3 = myField3;
    }

    @JsonPOJOBuilder(withPrefix = "")
    public static class ExtendingSomeClassBuilder extends SomeClass.SomeClassBuilder { }

}

Comments:

  • The parent class’s constructor have been converted to protected visibility to allow extension.
  • The builder class ExtendingSomeClassBuilder of the extending class is extending the parent’s SomeClass.SomeClassBuilder builder class for compilation reasons.

Happy building!

Leave a Reply

Your email address will not be published. Required fields are marked *