因为最近在做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 t = new Gson().fromJson(result, trueType);
当然程序并没有崩溃,最后在结果里面打印返回Result数据时,出现了(类转换异常)这个情况,当时我很费解,然后Debug一步一步的走,这时看到了一个惊奇的现象,List下并不是一个我们JavaBean的类型,而是LinkedTreeMap,这是我不禁返回去看上面解析到的Type类型是不是出错了,果然不出所料,竟然返回了Result<List
其实这里我纠结几个问题,Type type = new TypeToken<Result<List
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
Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
return new Gson().fromJson(reader, type);
}
public static
// 生成List
Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clazz});
// 根据List
Type type = new ParameterizedTypeImpl(Result.class, new Type[]{listType});
return new Gson().fromJson(reader, type);
}
这里还是让我有些疑惑,因为这两个解析方法最终还是需要传递实体类的Class,和最初的幻想越来越远了,我一直以为,能在调用的时候填入返回泛型,然后解析的时候根据泛型类型进行解析,果然还是想多了。。。
所以我这时在网络请求时,返回类型全指定为String,然后在返回结果的时候,再调用以上两个方法来解析成需要的实体类,后来转眼一想,这到最后还不是自己调用方法解析的么,总觉得这样玩失去了意义,好吧,既然不能通过泛型来直接解析实体类,那我干脆就直接传递实体类了:
我这里因为使用了Rxjava和Retrofit,所以我在网络请求这里写了这个转换类,并加入到请求队列,在返回的String数据后继续.map(new ResultFunc
//以下是封装的被观察者,没办法为了解析顺利,我这里传递了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
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);
}
}
既然传入了指定JavaBean类型,这里就已经可以用你传入的类解析成你Json数据了,所以这里对异常进行了处理,用于在你收到的data是JsonObject和JsonArray的区分,刚开始拿到返回的String数据时,先用JsonObject解析,如果抛出Json异常说明不是JsonObject类型,所以异常产生后,再用JsonArray去解析数据。
搞了半天,总觉得有些遗憾,不过总比之前稍微要好点吧,因为搞这些本来就没太大必要,因为Retrofit网络接口定义的时候本来就可以指定返回类型,不过折腾折腾总是好。