fastjson反序列化多层嵌套泛型类与java中的Type类型
public class ResultValue<T> {
private static final long serialVersionUID = 1L;
private String msg;
private String code;
private T data;
private boolean success;
}
public class ReportHttpClientUtils {
public static <T> ResultValue<T> sendHttpPostJson(String httpUrl, String paramsJson, Class<T> clz) {
String resultStr = HttpClientUtils.sendHttpPostJson(httpUrl,paramsJson);
return JSONObject.parseObject(resultStr,new TypeReference<ResultValue<T>>(clz){});
}
}
主要是 new TypeReference<ResultValue<T>>(clz){}
ResultValue<AccessTokenBean> resultValue = ReportHttpClientUtils.sendHttpPostJson("/getAccessToken",
参考
在使⽤springmvc时,我们通常会定义类似这样的通⽤类与前端进⾏交互,以便于前端可以做⼀些统⼀的处理:
public class Result<T> {
private int ret;
private String msg;
private T data;
// 此处省略getter和setter⽅法
}
这样的类序列化为json后,js反序列化处理起来毫⽆压⼒。但是如果rest接⼝的消费端就是java呢,java泛型的类型擦除却容易引⼊⼀些障碍。
⼀个反序列化的迭代
先定义⼀个类,后⾯的例⼦会⽤到:
public class Item {
private String name;
private String value;
// 此处省略getter和setter⽅法
}
JSON数据:
{
"data":{
"name":"username",
"value":"root"
},
"msg":"Success",
"ret":0
}
当拿到上⾯的数据时,我们想到其对应的类型是Result<Item>,所以得想办法将这个json数据反序列化为这个类型才⾏。
v1
JSONObject.parseObject(json, Result<Item>.class);,编译器就报错了Cannot select parameterized type。
v2
JSONObject.parseObject(json, Result.class);,执⾏没问题。但是没有Item类型信息,fastjson不可能跟你⼼有灵犀⼀点通知道该把data转为Item类型,Data().getClass()结果是com.alibaba.fastjson.JSONObject,也算是个妥善处理吧。
v3
了⼀下前⼈的经验,使⽤TypeReference来处理,JSONObject.parseObject(json, new TypeReference<Result<Item>>(){});,终于“完美”解决!
v4
有了v3的经验,以为到了通⽤处理的捷径,遂封装了⼀个处理这种类型的⼯具⽅法:
private static <T> Result<T> parseResultV1(String json) {
return JSONObject.parseObject(json, new TypeReference<Result<T>>() {
});
}
并且把采⽤v3的地⽅改⽤了此parseResult⽅法:
Result<Item> result = parseResultV1(json);
以为万事⼤吉,连测都没测试就把代码提交了。测都不测试,当然难以有好结果了:
System.out.Data());
// java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to Item
很显然parseResultV1把Item的类型信息丢掉了。
{
"data":"Hello,world!",
"msg":"Success",
"ret":0
}
试了⼀下Result形式的,parseResultV1可以成功将其反序列化。推测(没有看fastjson具体实现)是fastjson刚好检测到data字段就是String类型,并将其赋值到data字段上了。仔细看parseObject并没有报错,⽽是在getData()时报错的,联系到java的泛型擦除,我们在⽤getData(),应该把data当作Object类型这么看:
String data = (Data();
System.out.println(data);
v5
原来TypeReference的构造器是可以传⼊参数的,
private static <T> Result<T> parseResultV2(String json, Class<T> clazz) {
return JSONObject.parseObject(json, new TypeReference<Result<T>>(clazz) {
});
}
这个可以真的可以完美反序列化Result<Item>了。
v6
后来发现parseResultV2⽆法处理类似Result<List<T>>,原来TypeReference⽆法处理嵌套的泛型(这⾥指的是类型参数未确定,⽽不是类似Result<List<Item>>类型参数已经确定)。借⽤⾥的⽅法,新增加⼀个专门处理List类型的⽅法:
private static <T> Result<List<T>> parseListResult(String json, Class<T> clazz) {
return JSONObject.parseObject(json, buildType(Result.class, List.class, Item.class));
}
private static Type types) {
ParameterizedTypeImpl beforeType = null;
if (types != null && types.length > 0) {
for (int i = types.length - 1; i > 0; i--) {
beforeType = new ParameterizedTypeImpl(new Type[]{beforeType == null ? types[i] : beforeType}, null, types[i - 1]);
}
}
return beforeType;
}
或者根据这⾥只有两层,简单如下:
private static <T> Result<List<T>> parseListResult(String json, Class<T> clazz) {
ParameterizedTypeImpl inner = new ParameterizedTypeImpl(new Type[]{clazz}, null, List.class);
ParameterizedTypeImpl outer = new ParameterizedTypeImpl(new Type[]{inner}, null, Result.class);
return JSONObject.parseObject(json, outer);
}
v7
todo: 上⾯两个⽅法已经可以满⾜现有需要,有时间再看看能否将两个⽅法统⼀为⼀个。
com.alibaba.fastjson.TypeReference
看看TypeReference的源码:
protected actualTypeArguments) {
Class<?> thisClass = Class();
Type superClass = GenericSuperclass();
ParameterizedType argType = (ParameterizedType)((ParameterizedType)superClass).getActualTypeArguments()[0];
Type rawType = RawType();
Type[] argTypes = ActualTypeArguments();
int actualIndex = 0;
for(int i = 0; i < argTypes.length; ++i) {
if (argTypes[i] instanceof TypeVariable) {
argTypes[i] = actualTypeArguments[actualIndex++];
if (actualIndex >= actualTypeArguments.length) {
break;
}
}
}
Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType);
Type cachedType = ((key);
if (cachedType == null) {
classTypeCache.putIfAbsent(key, key);
cachedType = ((key);
}
}
实际上它⾸先获取到了泛型的类型参数argTypes,然后遍历这些类型参数,如果遇到是TypeVariable类型的则⽤构造函数传⼊的Type将其替换,然后此处理后的argTypes基于ParameterizedTypeImpl构造出⼀个新的Type,这样的新的Type就可以具备我们期待的Type的各个泛型类型参数的信息了。所以fastjson就能够符合我们期望地反序列化出了Result<Item>。
正是由于这个处理逻辑,所以对于v6⾥的Result<List<T>>就⽆法处理了,它只能处理单层多类型参数的情况,⽽⽆法处理嵌套的泛型参数。
没到TypeReference的有参构造函数⽤法的⽐较正式的⽂档,但是基于源码的认识,我们应该这么使⽤TypeReference的有参构造函数:
new TypeReference<Map<T1, T2>>(clazz1, clazz2){}
new TypeReference<Xxx<T1, T2, T3>>(clazz1, clazz2, clazz3){}
也就是构造器⾥的Type列表要与泛型类型参数⼀⼀对应。
com.alibaba.fastjson.util.ParameterizedTypeImpl
那⾄于ParameterizedTypeImpl怎么回事呢?
import flect.ParameterizedType;
// ...其他省略...
public class ParameterizedTypeImpl implements ParameterizedType {
public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType){
this.actualTypeArguments = actualTypeArguments;
this.ownerType = ownerType;
this.rawType = rawType;
}
// ...其他省略...
}
以前也没了解过ParameterizedType,与它相关的还有
Type
所有已知⼦接⼝:
GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType
所有已知实现类:
Class
先看看这次已经⽤到的ParameterizedType接⼝(下列注释是从jdk中⽂⽂档拷贝过来,不太好理解)
public interface ParameterizedType extends Type {
//返回表⽰此类型实际类型参数的 Type 对象的数组。
//注意,在某些情况下,返回的数组为空。如果此类型表⽰嵌套在参数化类型中的⾮参数化类型,则会发⽣这种情况。
Type[] getActualTypeArguments();
//返回 Type 对象,表⽰此类型是其成员之⼀的类型。
Type getOwnerType();
//返回 Type 对象,表⽰声明此类型的类或接⼝。
Type getRawType();
js获取json的key和value}
结合ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType)的例⼦来理解:
new ParameterizedTypeImpl(new Type[]{clazz}, null, List.class)⽤于构造List<T>。
关于Type
泛型是Java SE 1.5的新特性,Type也是1.5才有的。它是在java加⼊泛型之后为了扩充类型引⼊的。与Type相关的⼀些类或者接⼝来表⽰与Class类似但是⼜因泛型擦除丢失的⼀些类型信息。
ParameterizedTypeImpl使⽤踩坑
详见:
如果你觉得这篇⽂章对你有帮助或者使你有所启发,请点击右下⾓的推荐按钮,谢谢,:)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论