Using Jackson for JSON Serialization and Deserialization

1. Introduction

Jackson is one of the most common Java libraries for processing JSON. It is used for reading and writing JSON among other tasks. Using Jackson, you can easily handle automatic conversion from Java objects to JSON and back. In this article, we delve into some common Jackson usage patterns.

2. Converting a POJO to JSON

Suppose we want to convert a sample POJO (Plain Old Java Object) to JSON. An example class is defined below.

public class User {
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private List<String> emailAddrs;

    public User(String firstName,String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // standard getters and setters here
}

Converting such an object to JSON is just a couple of lines:

ObjectMapper mapper = new ObjectMapper();
User user = new User("Harrison", "Ford");
user.setEmailAddrs(Arrays.asList("harrison@example.com"));
mapper.writeValue(System.out, user);

// prints:
{"firstName":"Harrison","lastName":"Ford","dateOfBirth":null,"emailAddrs":["harrison@example.com"]}

3. Pretty Printing

Note that the default output is quite compact. Sometimes it is useful to be able to view indented output for debugging purposes. To produce properly indented output, enable the option SerializationFeature.INDENT_OUTPUT on the ObjectMapper instance before using it to serialize the object.

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
User user = new User("Harrison", "Ford");
...

The output generated now is nicely indented:

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : null,
  "emailAddrs" : [ "harrison@example.com" ]
}

4. Ignore NULL fields

How do we tell Jackson not to serialize null values (as for “dateOfBirth” above)? There are a couple of ways. You can tell the ObjectMapper to skip all NULL fields, or you can use annotations to specify per class or per field.

4.1. Configuring ObjectMapper

...
mapper.setSerializationInclusion(Include.NON_NULL);
User user = new User("Harrison", "Ford");
...

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "emailAddrs" : [ "harrison@example.com" ]
}

4.2. With an Annotation

Use the annotation @JsonInclude(Include.NON_NULL) on the target object class to eliminate serialization of all NULL fields.

@JsonInclude(Include.NON_NULL)
public class User {
    private String firstName;
    private String lastName;
    ...
}

Or on a field (or property) to disable specific NULL fields from being serialized. In the code below, dateOfBirth will be ignored if null, but not heightInM.

...
    @JsonInclude(Include.NON_NULL)
    private Date dateOfBirth;

    private Float heightInM;
...

5. Ignore Empty Arrays

When you have a class with an empty array initializer, the value is serialized as an empty JSON array.

class User {
    ...
    private List<String> phoneNumbers = new ArrayList<>();
    ...
}

// serialized to:
{
  ...
  "phoneNumbers" : [ ],
  ...
}

When you want to skip empty array declarations being output, you can use the following.

mapper.setSerializationInclusion(Include.NON_EMPTY);

6. Generate JSON String

How can we generate a JSON string representation instead of writing it directly to a file or an OutputStream? Maybe you want to store the JSON string in a database. Use the writeValueAsString() method.

// set up user
String jsonStr = mapper.writeValueAsString(user);

7. Formatting Dates

By default, Jackson prints Date fields as numeric timestamps as shown below:

Calendar c = Calendar.getInstance();
c.clear(); c.set(1942, 6, 13);
user.setDateOfBirth(c.getTime());

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : -866957400000,
  "emailAddrs" : [ "harrison@example.com" ]
}

Turning off numeric timestamp results in serialization of a date in the ISO 8601 format as shown below:

...
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
...

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "1942-07-12T18:30:00.000+0000",
  "emailAddrs" : [ "harrison@example.com" ]
}

Change the default date format by using SimpleDateFormat as shown:

...
DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy");
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
...

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "13-Jul-1942",
  "emailAddrs" : [ "harrison@example.com" ]
}

8. Converting JSON to POJO

Reading and converting JSON to a Java object is just as easy with Jackson. Accomplished in just a couple of lines:

ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File(jsonFile), User.class);

The target class (User in this case) needs a no-arguments default constructor defined – failing which you get this exception:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of sample.User: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

The situation is easily address by adding a no-arguments default constructor.

public class User {
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private List<String> emailAddrs;

    public User() {}
    ...
}

9. Adjusting Date Format

As before, the date format needs to be adjusted unless it is in ISO 8601 format. Parsing the following JSON fails:

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "13-Jul-1942",
  "emailAddrs" : [ "harrison@example.com", null ],
}

The following exception is reported:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "13-Jul-1942": not a valid representation (error: Failed to parse Date value '13-Jul-1942': Can not parse date "13-Jul-1942": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))

Specify the date format if you are using a non-standard format as shown:

DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy");
mapper.setDateFormat(fmt);

10. Ignoring Unknown Properties

Sometimes the JSON you are trying to read might include some properties not defined in the Java class. Maybe the JSON was updated but the change is not yet reflected in the Java class. In such cases, you might end up with an exception like this:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "gender" (class sample.User), not marked as ignorable (6 known properties: ...)

You can tell Jackson to ignore such properties on a global level by disabling a deserialization feature as follows:

mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

Alternatively, you can use the following annotation on the class to ignore unknown properties.

@JsonIgnoreProperties(ignoreUnknown=true)
public class User {
    ...
}

11. Serializing Array of Objects to JSON

Serializing an array of objects to JSON is straightforward.

ObjectMapper mapper = new ObjectMapper();
List<User> users = new ArrayList<>();
users.add(new User(...));
users.add(new User(...));
System.out.println(mapper.writeValueAsString(users));

The array is properly serialized as shown:

[ {
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "13-Jul-1942",
  ...},
  {
  "firstName" : "Samuel",
  "lastName" : "Jackson",
  "dateOfBirth" : "21-Dec-1948",
  ...} ]

12. Deserialize Array of Objects from JSON

Several methods are available to deserialize a JSON array to a collection of Java objects. Use whatever method suits you depending on your requirements.

Deserializing a JSON array to a Java array of objects of a particular class is as simple as:

User[] users = mapper.readValue(new File(jsonFile), User[].class);
System.out.println(mapper.writeValueAsString(users));

Using a JavaType is useful when constructing collections or parametric types.

JavaType listType = mapper
    .getTypeFactory()
    .constructCollectionType(List.class, User.class);
List<User> users = mapper.readValue(new File(jsonFile), listType);
System.out.println(mapper.writeValueAsString(users));

A third method is to create and use a TypeReference.

TypeReference ref = new TypeReference<List<User>>(){};
List<User> users = mapper.readValue(new File(jsonFile), ref);
System.out.println(mapper.writeValueAsString(users));

Summary

This article showed you how to convert Java objects to JSON and back. We also covered a few common use cases.