Course – LS (cat=JSON/Jackson)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Background

Jackson is a popular Java library to serialize Java objects to JSON and vice versa. In some cases, the Java objects could be defined with a generic type.

In this tutorial, we’ll illustrate how to use Jackson to deserialize a JSON string into a generic type.

2. Models Preparation

For the given JSON string to be deserialized:

{"result":{"id":1,"firstName":"John","lastName":"Lewis"}}

we need to define a class with a generic type parameter and a regular POJO object to hold the data:

public class JsonResponse<T> {
    private T result;

    // getters and setters...
}
public class User {
    private Long id;
    private String firstName;
    private String lastName;

    // getters and setters...
}

3. Deserialize Generic Type

In Jackson, ObjectMapper provides three sets of readValue methods for JSON deserialization that take:

  • Class<T> as a parameter to pass the type of information
  • TypeReference to pass the type information
  • JavaType as the parameter

We can’t use JsonResponse<User>.class to pass to methods from the first bullet point, so let’s see how to do generics deserialization using TypeReference and JavaType.

3.1. TypeReference

As well known, Java erases generic type information during compilation, but we can take advantage of the power of anonymous inner classes to preserve the type information during compile time. Jackson provides the abstract class TypeReference to obtain the type information from the derivated subclasses:

public abstract class TypeReference<T> { 
    protected final Type _type;

    protected TypeReference() {
        Type superClass = this.getClass().getGenericSuperclass();
        this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0]; 
    } 
}

With TypeReference, we can create an anonymous inner class for the generic type JsonResponse<User> as follows:

TypeReference<JsonResponse<User>> typeRef = new TypeReference<JsonResponse<User>>() {};

This approach for preserving the generic type information is known as a super type token. By using super type tokens, Jackson will know that the container type is JsonResponse and its type parameter is User. 

Here is the complete test case for the deserialization:

@Test
void givenJsonObject_whenDeserializeIntoGenericTypeByTypeReference_thenCorrect() throws JsonProcessingException {
    String json = "{\"result\":{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Lewis\"}}";

    TypeReference<JsonResponse<User>> typeRef = new TypeReference<JsonResponse<User>>() {};
    JsonResponse<User> jsonResponse = objectMapper.readValue(json, typeRef);
    User user = jsonResponse.getResult();

    assertThat(user.getId()).isEqualTo(1);
    assertThat(user.getFirstName()).isEqualTo("John");
    assertThat(user.getLastName()).isEqualTo("Lewis");
}

3.2. JavaType

If the type parameter T is not static, we need to choose JavaType instead of TypeReference to pass the type information for deserialization. ObjectMapper provides such methods, which are recommended now from Jackson 2.5, and we can use TypeFactory to construct the JavaType object with our type parameter:

JavaType javaType = objectMapper.getTypeFactory().constructParametricType(JsonResponse.class, User.class);
JsonResponse<User> jsonResponse = objectMapper.readValue(json, javaType);

Here User.class is passed as the second parameter to the method constructParametricType, and it can be easily changed to other parameterized types.

4. Conclusion

In this article, we have introduced two simple ways to deserialize a JSON string into an object with a generic type.

As usual, all code snippets presented in this article are available on over on GitHub.

Course – LS (cat=JSON/Jackson)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – Jackson (eBook) (cat=Jackson)
Comments are closed on this article!