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 中,我们一般使用 ObjectMapper
的 readValue
方法来反序列化JSON,其中第二个参数类型支持下面3种:
-
Class<T>
-
TypeReference
-
JavaType
我们用到最多的是第一种,但这里不能直接使用 JsonResponse<User>.class
,所以让我们看看如何使用 TypeReference
和 JavaType
实现对泛型的反序列化。
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 项目 上可以找到。