根据泛型动态构造jackson的TypeReference
引出问题
- 将json字符串反序列化为带泛型的类的对象怎么操作?
- 怎么动态构造`TypeReference对象?
使用TypeReference反序列化的例子
有以下类定义:
class Result<T> {
private long code;
private T data;
}
class People {
private String name;
}
实例化以下对象:
Result<List<People>> result = new Result<>();
List<People> peopleList = new ArrayList<>();
People xiaoHong = new People("小红");
People xiaoMing = new People("小明");
peopleList.add(xiaoHong);
peopleList.add(xiaoMing);
result.setData(peopleList);
其对应的JSON字符串为:
{"code":0,"data":[{"name":"小红"},{"name":"小明"}]}
下面使用jackson的TypeReference来反序列化字符串为以上的对象:
String jsonStr = "{\"code\":0,\"data\":[{\"name\":\"小红\"},{\"name\":\"小明\"}]}";
TypeReference<Result<List<People>>> typeReference = new TypeReference<Result<List<People>>>() {};
ObjectMapper objectMapper = new ObjectMapper();
Result<List<People>> listResult = objectMapper.readValue(jsonStr, typeReference);
当我们构造http客户端的时候,有时候不想让用户看到类似Result<List<People>>
这样的返回,只想给用户List<People>
这样的数据,那怎么动态构造TypeReference呢?
根据泛型动态构造TypeReference
我们还是以一个实际的例子来说明,假设我们提供了一个http客户端工具,它的定义是这样的。
class HttpClient {
public <T> T get(String url, Map<String,Object> params, TypeReference<T> typeR) {
//获取jsonStr, 假设这里获取到的是:{"code":0,"data":[{"name":"小红"},{"name":"小明"}]}
String jsonStr = execute(url, params);
//todo 解析为枚举类型,下面会讲解如何实现
}
}
假设我们这样调用HttpClient:
HttpClient client = new HttpClient();
//注意这里的TypeReference的泛型
List<People> result = client.get("http://xxxx", params, new TypeReference<List<People>>());
额额额???为啥不是TypeReference<Result<List<People>>>
???
这就是我们要达到的目的,即用户不关注外层包裹的Result,只需要指定实际想要的类型即可。下面我们来看下如何实现。
带泛型的类如何表示?
首先我们实现一个ParameterizedType的子类,至于这个类的作用请往下看:
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class ParameterizedTypeImpl implements ParameterizedType, Serializable {
private final Type[] actualTypeArguments;
private final Type ownerType;
private final Type rawType;
public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType) {
this.actualTypeArguments = actualTypeArguments;
this.ownerType = ownerType;
this.rawType = rawType;
}
@Override
public Type[] getActualTypeArguments() {
return this.actualTypeArguments;
}
@Override
public Type getRawType() {
return this.rawType;
}
@Override
public Type getOwnerType() {
return this.ownerType;
}
}
我们来解释一下这个类,先看一下这个类的继承链:
ParameterizedTypeImpl implements ParameterizedType extends Type
- Type接口的语义是指:类型,标识java里的所有类。(注意:Class类也是Type的实现类,下面会用到)
- ParameterizedType接口的语义是:带泛型的类。
在java9之前jdk是包含了ParameterizedTypeImpl的实现的,但在jdk9及以后就没了,所以保险起见我们自己需要实现一下。
我们再看一下TypeReference的定义:
public abstract class TypeReference<T> implements Comparable<TypeReference<T>> {
protected final Type _type;
protected TypeReference() {
Type superClass = this.getClass().getGenericSuperclass();
if (superClass instanceof Class) {
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
} else {
this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0];
}
}
public Type getType() {
return this._type;
}
public int compareTo(TypeReference<T> o) {
return 0;
}
}
这里的_type
成员变量即ParameterizedTypeImpl类型。
我们继续看ParameterizedTypeImpl几个成员变量的意思:(此处以:new TypeReference<Result<List<People>>>
为例)
- rawType
此处的rowType=Result.class
,即原始的类是什么 - actualTypeArguments
此处的actualTypeArguments=new Type[]{List<People>.class}
,为什么是数组类型,是因为泛型里面可以放多个类型,我可以这么放:Result<List<People>,Set<People>>
,那么这里的actualTypeArguments=new Type[]{List<People>.class,Set<People.class>}
,因为是一个Type数组,而每个Type又是一个ParameterizedType,所以可以向下继续查看(嵌套)。 - ownerType
所谓的ownerType只在嵌套类中会出现,其他情况为空。举个例子:I<T>.S<E>
,则S<E>
的ownerType=I<T>
完成HttpClient的实现
class HttpClient {
public <T> T get(String url, Map<String,Object> params, TypeReference<T> typeR) {
//获取jsonStr, 假设这里获取到的是:{"code":0,"data":[{"name":"小红"},{"name":"小明"}]}
String jsonStr = execute(url, params);
ObjectMapper objectMapper = new ObjectMapper();
//在此处偷梁换柱(所谓的动态构造其实就一句话)
TypeReference<Result<T>> resultType = new TypeReference<Result<T>>>() {
@Override
public Type getType() {
//在外层再包一层Result,就是所谓的动态构造,是不是很简单!!!
return new ParameterizedTypeImpl(new Type[]{typeR.getType()},null, Result.class);
}
};
Result<T> listResult = objectMapper.readValue(jsonStr, resultType);
//此处省略了一下异常判断,不可直接这么使用
return listResult.getData();
}
}
然后我们就可以这么调用了:
HttpClient client = new HttpClient();
//注意这里的TypeReference的泛型
List<People> result = client.get("http://xxxx", params, new TypeReference<List<People>>());
OK,完结!