之前一直是用Gson做数据序列化,后来换工作后新公司是用fastJson做序列化的,在做网络框架优化后,不定时的会抛出JsonObject can't covert to **** 异常,各种排查后发现是fastJson的问题,下面一步一步来分析:
原始json数据:
//登录前
{"code":200,"msg":"成功","data":[]}
//登录后
{"code":200,"msg":"成功","data":{"count":0}}
解析json代码:
static CommonResponse responseConvert(String json, Type clz){
JSONObject object = new JSONObject(json);
String data = object.optString(DATA);
if(TextUtils.isEmpty(data) || "[]".equals(data) || "{}".equals(data) ){
CommonResponse resp = JSON.parseObject(json, CommonResponse.class);
....
return resp;
}else{
return JSON.parseObject(json,clz)
}
}
分析原因:
从代码上来看应该没问题,看不出什么问题,首先判断json的data字段是否为空或是否为空数组,如果是空则使用默认的CommonResponse类去解析,如果不为空则使用CommonResponse<T>泛型的方式去解析,如果data字段一直为空或一直不为空都不会有问题,但如果从空变成有数据后就出现了上面的异常,通过查看fastJson源码才发现问题所在:
ParserConfig.Java
public ObjectDeserializer getDeserializer(Type type) {
...省略无关代码
if (type instanceof Class<?>) {
return getDeserializer((Class<?>) type, type);
}
if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class<?>) {
return getDeserializer((Class<?>) rawType, type);
} else {
return getDeserializer(rawType);
}
}
return JavaObjectDeserializer.instance;
}
通过源码可以看到,首先判断参数type是否为Class,如果是则进入getDeserializer方法,如果不是则继续判断参数是否为泛型,getRawType是获取泛型类型,如果泛型里面没有再嵌套泛型了则也进入getDeserializer方法,如果还有则进行递归调用,直到拿到最里面泛型为止,继续看getDeserializer方法:
public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
...省略无关代码
if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
derializer = derializers.get(clazz);
}
if (derializer != null) {
return derializer;
}
...省略无关代码
putDeserializer(type, derializer);
return derializer;
}
在getDeserializer方法中会先去判断缓存中是否已存储这个参数Class如果有则直接用缓存中的,如果没有则添加到缓存中。
代码看到这里也就发现问题所在了,原因:首先在第一次解析CommonResponse的时候走的是getDeserializer的第一个if,然后缓存起来这个CommonResponse,接着在登录后,data字段数据类型发生了改变,变成CommonResponse<T>类型,所以解析的走的是第二个if,结果rawType是CommonResponse,所以直接从缓存中返回了第一次解析的结果,这样就相当于丢失了CommonResponse中的泛型<T>,所以导致最后类型转换失败!
总结
统一使用泛型类型,不允许没有泛型类型的CommonResponse,如果不能统一泛型类型的,最好换一个Json解析库!