Jackson ObjectMapper default values with lombok on codhaus and fasterexml

Viewed 1147

I was using org.codehaus.jackson.map.ObjectMapper to deserialize a JSON string with default values as follows:

I have the following POJO:

@Data
@Builder
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class CompanyRequest {
    @Builder.Default
    private String email = "";
    private int company;
    @Builder.Default
    List<UserIdMapping> users = new ArrayList<>();
}

and when executing the following:

CompanyRequest companyRequest = objectMapper.readValue(message, CompanyRequest.class);

where message is:

{
  "email": "john@doe.com",
  "company": 42,
}

it worked as expected, meaning companyRequest initialized with users as empty ArrayList.

then I switched to com.fasterxml.jackson.databind.ObjectMapper, but users for the same message was null.

I was able to solve it with the following changes:

@Data
@EqualsAndHashCode
@Builder
public class CompanyRequest {
    private String email;
    private int company;
    private List<UserIdMapping> users;

    @JsonCreator
    public CompanyRequest() {
        this.email = "";
        this.users = new ArrayList<>();
    }

    @Builder
    public CompanyRequest(String email, int company, List<UserIdMapping> users) {
        this.sfAccountId = StringUtils.isEmpty(email) ? "" : email;
        this.company = company;
        this.users = CollectionUtils.isEmpty(users) ? new ArrayList<>() : users;
    }
}

but after knowing that codehaus is actually working as intended without this manipulation I was wondering is there a cleaner way to do it with fasterxml, since fasterxml is the newer version of ObjectMapper, meaning is it a feature that enables it?

2 Answers

The "codehaus" version of Jackson is the 1.x version of Jackson. Since 2.0, Jackson is located at "com.fasterxml.jackson". Without having tested it, it is very likely that the behavior simply (and intentionally) changed with Jackson 2.

However, as you found out by yourself, you can explicitly advise Jackson to use the no-args constructor when deserializing. You can also put that annotation on a lombok-generated constructor, so you do not have to write it manually:

@Data
@Builder
@EqualsAndHashCode
@NoArgsConstructor(onConstructor_ = @JsonCreator)
@AllArgsConstructor
public class CompanyRequest {
    @Builder.Default
    private String email = "";
    private int company;
    @Builder.Default
    List<UserIdMapping> users = new ArrayList<>();
}

Make sure you use at least Lombok v1.18.2 so ensure the @Builder.Default values are also set within this constructor.

Consider putting @Jacksonized @Builder on the class if you want immutability and the builder to be the only way of creating instances (with at least Lombok v1.18.16):

@Jacksonized
@Builder
@Value
public class CompanyRequest {
    @Builder.Default
    private String email = "";
    private int company;
    @Singular
    List<UserIdMapping> users;
}

Use the @Jacksonized annotation provided by lombok

Related