因为最近在做Gson解析网络数据的时候遇到一个现象,当我们在服务器拿到的Json数据,一般格式都是比较统一的,只是内容有一定的变化,具体事例如下:
// data 为 object 的情况
{"code":"0","message":"success","data":{}}
// data 为 array 的情况
{"code":"0","message":"success","data":[]}
所以,我们一般把需要变动的数据模型用泛型表示:
public class Result<T> {
public String code;
public String message;
public T data;
}
public class JavaBean{
public String name;
public int age;
}
然后在泛型解析的时候出问题了,为了获取T的实际类型,用了如下代码:
Type type = new TypeToken<Result<List<T>>>() {}.getType();
T t = new Gson().fromJson(result, trueType);
当然程序并没有崩溃,最后在结果里面打印返回Result数据时,出现了(类转换异常)这个情况,当时我很费解,然后Debug一步一步的走,这时看到了一个惊奇的现象,List下并不是一个我们JavaBean的类型,而是LinkedTreeMap,这是我不禁返回去看上面解析到的Type类型是不是出错了,果然不出所料,竟然返回了Result<List<T>>类型,并不是我们传入的JavaBean类型,这是因为Java的泛型擦除机制,如果不了解的,可以百度一下哈,文章很多。
其实这里我纠结几个问题,Type type = new TypeToken<Result<List<T>>>() {}.getType();这句代码中,Result<List<T>>是我们指定的类型,既然要给指定类型,那么他存在的意义是什么呢?
new Gson().fromJson(result, JavaBean.class);
上面这句代码一样可以解析成实体类,既然都必须指明实体类类型,何必用上面代码先获取Type呢?这里我很疑惑。。。
然后网上找资料,解决方案都大同小异,我这里使用下面这种:
public class ParameterizedTypeImpl implements ParameterizedType {
private final Class raw;
private final Type[] args;
public ParameterizedTypeImpl(Class raw, Type[] args) {
this.raw = raw;
this.args = args != null ? args : new Type[0];
}
@Override
public Type[] getActualTypeArguments() {
return args;
}
@Override
public Type getRawType() {
return raw;
}
@Override
public Type getOwnerType() {
return null;
}
}
public static <T> Result<T> fromJsonObject(String reader, Class<T> clazz) {
Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
return new Gson().fromJson(reader, type);
}
public static <T> Result<List<T>> fromJsonArray(String reader, Class<T> clazz) {
// 生成List<T> 中的 List<T>
Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clazz});
// 根据List<T>生成完整的Result<List<T>>
Type type = new ParameterizedTypeImpl(Result.class, new Type[]{listType});
return new Gson().fromJson(reader, type);
}
这里还是让我有些疑惑,因为这两个解析方法最终还是需要传递实体类的Class,和最初的幻想越来越远了,我一直以为,能在调用的时候填入返回泛型,然后解析的时候根据泛型类型进行解析,果然还是想多了。。。
所以我这时在网络请求时,返回类型全指定为String,然后在返回结果的时候,再调用以上两个方法来解析成需要的实体类,后来转眼一想,这到最后还不是自己调用方法解析的么,总觉得这样玩失去了意义,好吧,既然不能通过泛型来直接解析实体类,那我干脆就直接传递实体类了:
我这里因为使用了Rxjava和Retrofit,所以我在网络请求这里写了这个转换类,并加入到请求队列,在返回的String数据后继续.map(new ResultFunc<T>(beanClass))转换成需要的JavaBean。
//以下是封装的被观察者,没办法为了解析顺利,我这里传递了JavaBean的Class
public Observable<String> loadString(String url,Class beanClass) {
return mService
.loadString(url)
.map(new StringFunc())
.map(new ResultFunc<T>(beanClass));
}
//********************************************以下是转换类*****************************************************
public class ResultFunc<T> implements Func1<String, Result<T>> {
Class beanClass;
public ResultFunc(Class beanClass) {
this.beanClass = beanClass;
}
@Override
public Result<T> call(String result) {
Result<T> t = null;
try {
t = (Result<T>) fromJsonObject(result, beanClass);
} catch (JsonSyntaxException e) {//解析异常,说明是array数组
t = (Result<T>) fromJsonArray(result, beanClass);
}
return t;
}
public static <T> Result<T> fromJsonObject(String reader, Class<T> clazz) {
Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
return new Gson().fromJson(reader, type);
}
public static <T> Result<List<T>> fromJsonArray(String reader, Class<T> clazz) {
// 生成List<T> 中的 List<T>
Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clazz});
// 根据List<T>生成完整的Result<List<T>>
Type type = new ParameterizedTypeImpl(Result.class, new Type[]{listType});
return new Gson().fromJson(reader, type);
}
}
网友评论