1. 背景

Jackson 是一个流行的 Java 库,用于将 Java 对象序列化为 JSON 以及反序列化。

在反序列化时,如果Java对象使用了泛型类型,IDE通常会提示 "Unchecked assignment" 的警告, 本教演示如何解决这个问题。

2. 准备工作

假设,我们要解析的 JSON 字符串如下:

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

我们需要定义对应的POJO类。这里 JsonResponse 是一个泛型:

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. 反序列化泛型类型

在 Jackson 中,我们一般使用 ObjectMapperreadValue 方法来反序列化JSON,其中第二个参数类型支持下面3种:

  • Class<T>
  • TypeReference
  • JavaType

我们用到最多的是第一种,但这里不能直接使用 JsonResponse<User>.class,所以让我们看看如何使用 TypeReferenceJavaType 实现对泛型的反序列化。

3.1. TypeReference

众所周知,Java 在编译时会擦除泛型类型信息,但我们可以通过利用匿名内部类在编译时保留类型信息。Jackson 提供了抽象类 TypeReference 来从派生子类中获取类型信息,源码摘要如下:

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

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

使用 TypeReference,我们可以为泛型类型 JsonResponse<User> 创建一个匿名内部类如下:

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

这种保存泛型类型信息的方法被称为 super type token。通过使用超类token,Jackson 将知道容器类型是 JsonResponse,其类型参数是 User

以下是完整的反序列化测试用例:

@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

如果类型参数 T 不是静态的,我们需要选择 JavaType 而不是 TypeReference 来传递反序列化时的类型信息。ObjectMapper 提供了这些推荐方法,自 Jackson 2.5 开始提供,并且我们可以使用 TypeFactory 构造带有我们类型参数的 JavaType 对象:

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

这里将 User.class 作为 constructParametricType 方法的第二个参数传递,它很容易更改为其他参数化类型。

4. 总结

在这篇文章中,我们介绍了两种简单的方法,将 JSON 字符串反序列化为具有泛型类型的对象。

官网,本文中的所有代码片段都在 GitHub 项目 上可以找到。