Using the Builder Pattern in Jackson

Builder

Using a builder pattern for your classes is a useful pattern when you have a large number of optional parameters. Static factories and constructors have a limitation; they do not scale very well when there a large number of optional parameters. Within this post I look at a basic builder pattern and how we can use the builder pattern with Jackson.

Traditionally if you have a contact you want to represent you may do something like this:

public class Contact {
    private final int id;
    private final String firstname;
    private final String lastname;
    private final String telephone;
    private final String mobile;

    public Contact() {
        this.id = 0;
        this.firstname = null;
        this.lastname = null;
        this.telephone = null;
        this.mobile = null;
    }

    public Contact(
            int id,
            String firstname, 
            String lastname, 
            String telephone, 
            String mobile) {
        
        this.id = id;
        this.firstname = firstname;
        this.lastname = lastname;
        this.telephone = telephone;
        this.mobile = mobile;
    }

    public int getId() {
        return id;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public String getTelephone() {
        return telephone;
    }

    public String getMobile() {
        return mobile;
    }
}

Programmers have traditionally used a telescoping constructor pattern, for which you provide a constructor with only the required parameters, another with a single optional parameter, a third with two optional parameters, and so on. As a result you end up with something like the following:

public Contact() {
        this.id = 0;
        this.firstname = null;
        this.lastname = null;
        this.telephone = null;
        this.mobile = null;
    }

    public Contact(
            int id) {
        
        this(id, null, null, null, null);
    }

    public Contact(
            int id, 
            String firstname) {
        
        this(id, firstname, null, null, null);
    }

    public Contact(
            int id, 
            String firstname, 
            String lastname) {
        
        this(id, firstname, lastname, null, null);
    }

    public Contact(
            int id, 
            String firstname, 
            String lastname, 
            String telephone) {
        
        this(id, firstname, lastname, telephone, null);
    }
    
    public Contact(
            int id,
            String firstname,
            String lastname,
            String telephone,
            
            String mobile) {

        this.id = id;
        this.firstname = firstname;
        this.lastname = lastname;
        this.telephone = telephone;
        this.mobile = mobile;
    }

Whilst the telescoping constructor pattern works it is hard to read the resulting code, difficult to deal with changes in the number of optional parameters. It is very easy to loose track of what parameter is being set

Contact myContact = new Contact(
    1, 
    "Joe", 
    "Bloggs",
    null,
    "123456789"
);

Contrast the above telescoping constructor pattern with a builder pattern approach. With a builder pattern approach initializing a new Contact becomes:

Contact myContact = new Contact.Builder(1)
                .firstname("Joe")
                .lastname("Bloggs")
                .mobile("123456789")
                .build();

Builder Pattern

So how do you write a class which allows us to use this approach? Read on and I will show you an example and I will also let you know how to adapt the pattern so it works in Jackson.

public class Contact {
    private final int id;
    private final String firstname;
    private final String lastname;
    private final String telephone;
    private final String mobile;

    public static class Builder {
        // Required parameters
        private final int id;

        // Optional parameters - initialized to default values
        private String firstname = null;
        private String lastname = null;
        private String telephone = null;
        private String mobile = null;

        public Builder(@JsonProperty("id") int id) {
            this.id = id;
        }

        public Builder firstname(String val) {
            firstname = val;
            return this;
        }

        public Builder lastname(String val) {
            lastname = val;
            return this;
        }

        public Builder telephone(String val) {
            telephone = val;
            return this;
        }
        
        public Builder mobile(String val) {
            mobile = val;
            return this;
        }

        public Contact build() {
            return new Contact(this);
        }
    }

    private Contact(Builder builder) {
        id = builder.id;
        firstname = builder.firstname;
        lastname = builder.lastname;
        telephone = builder.telephone;
        mobile = builder.mobile;
    }


    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstname;
    }

    public String getLastName() {
        return lastname;
    }

    public String getTelephone() {
        return telephone;
    }
    
    public String getMobile() {
        return mobile;
    }
}

The client code is now easier to write and read. The builder’s setter methods return the builder itself so that invocations can be chained together as we showed before:

Contact myContact = new Contact.Builder(1)
                .firstname("Joe")
                .lastname("Bloggs")
                .mobile("123456789")
                .build();

Using the Builder Pattern in Jackson

Attempt to use this pattern from a Jackson class however though and you will hit a problem.

ERROR [2016-05-24 14:24:41,420] io.dropwizard.jersey.jackson.JsonProcessingExceptionMapper: Unable to deserialize the specific type
! com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.dwbook.phonebook.representations.Contact]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)

In Jackson 2+ there is built in support for the builder pattern, you will need to annotate the class a little though.

First you need to say what Builder class is being used, as follows:

@JsonDeserialize(builder = Contact.Builder.class)

If your builder methods are in the form withXxx it works as is, otherwise you need to specify the format of the builder methods using an annotation:

@JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")

In my case the builder methods are not prefixed and my build method is called ‘build’. My Builder constructor has one required field ‘id’ so I use the @JsonProperty annotation to let Jackson know how to map the ‘id’ field.

 public Builder(@JsonProperty("id") int id) {
            this.id = id;
        }

The complete example is shown below:

@JsonDeserialize(builder = Contact.Builder.class)
public class Contact {
    private final int id;
    private final String firstname;
    private final String lastname;
    private final String telephone;
    private final String mobile;

    @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
    public static class Builder {
        // Required parameters
        private final int id;

        // Optional parameters - initialized to default values
        private String firstname = null;
        private String lastname = null;
        private String telephone = null;
        private String mobile = null;

        public Builder(@JsonProperty("id") int id) {
            this.id = id;
        }

        public Builder firstname(String val) {
            firstname = val;
            return this;
        }

        public Builder lastname(String val) {
            lastname = val;
            return this;
        }

        public Builder telephone(String val) {
            telephone = val;
            return this;
        }

        public Builder mobile(String val) {
            mobile = val;
            return this;
        }

        public Contact build() {
            return new Contact(this);
        }
    }

    private Contact(Builder builder) {
        id = builder.id;
        firstname = builder.firstname;
        lastname = builder.lastname;
        telephone = builder.telephone;
        mobile = builder.mobile;
    }


    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstname;
    }

    public String getLastName() {
        return lastname;
    }

    public String getTelephone() {
        return telephone;
    }

    public String getMobile() {
        return mobile;
    }
}

Now with this in place you can use the builder pattern with Jackson and enjoy cleaner classes.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

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