作者: @怪盗kidou
如需转载需在明显位置保留作者信息及原文链接
如果博客中有不恰当之处欢迎留言交流
http://www.jianshu.com/p/d62c2be60617
在你真的会用Gson吗?Gson使用指南(一) 的第三节我介绍了在Gson中如何使用泛型来简化我们的类设计,但随之而来引入了一个新的问题:封装。不知道各位有没有想过这样一个问题:每次都要用 new TypeToken<XXX>(){};
好麻烦,有没有更好的办法?
有更好的办法么?当然有!相信也有不少人自己作了尝试,只是有人欢喜有人愁了,不过没关系,今天我们就来解决这个问题。
约定
1、本文涉及到的json格式
// data 为 object 的情况
{"code":"0","message":"success","data":{}}
// data 为 array 的情况
{"code":"0","message":"success","data":[]}
2、假定第一种的对应的Java类型为 Result<XXX>
,第二种为 Result<List<XXX>>
一、为何封装,如何封装
1、为何封装:
- 写
new TypeToken<XXX>(){}
麻烦,IDE格式化后还不好看 - 不同的地方每进行一次
new TypeToken<XXX>(){}
操作都会生成一个新的类 - 对于任意类
XXX
都只有两种情况new TypeToken<Result<XXX>>(){}
和new TypeToken<Result<List<XXX>>>(){}
- 方便统一管理
2、如何封装
从上面的我们可以知道,最简单的方法就是提供两个方法分别对应data
为Array和Object的情况并接收一个参数,即告知XXX的类型,自动将完成new TypeToken<XXX>(){}
与new TypeToken<Result<List<XXX>>>(){}
的过程。
方法原型:
// 处理 data 为 object 的情况
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {}
// 处理 data 为 array 的情况
public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz){}
二、为何失败?
对于那些尝试着封装过的人可能都这么写过:
public static <T> Result<List<T>> fromJsonArray(Reader reader) {
Type type = new TypeToken<Result<List<T>>>(){}.getType();
return GSON.fromJson(reader, type);
}
当然上面的写法肯定是没有办法完成的,虽然代码不会报错,但运行结果肯定是不对的,因为这里的T
其实是一个 TypeVariable
,他在运行时并不会变成我们想要的XXX,所以通过TypeToken
得到的 泛型信息只是 "Result<List<T>>"
。
三、如何解决?
既然TypeToken的作用是用于获取泛型的类,返回的类型为Type
,真正的泛型信息就是放在这个Type
里面,既然用TypeToken生成会有问题,那我们自己生成Type就行了嘛。
Type是Java中所有类型的父接口,在1.8以前是一个空接口,自1.8起多了个getTypeName()
方法,下面有ParameterizedType
、 GenericArrayType
、 WildcardType
、 TypeVariable
几个接口,以及Class
类。这几个接口在本次封装过程中只会用到 ParameterizedType
,所以简单说一下:
ParameterizedType
简单说来就是形如“ 类型<> ”的类型,如:Map<String,User>
。下面就以 Map<String,User>
为例讲一下里面各个方法的作用。
public interface ParameterizedType extends Type {
// 返回Map<String,User>里的String和User,所以这里返回[String.class,User.clas]
Type[] getActualTypeArguments();
// Map<String,User>里的Map,所以返回值是Map.class
Type getRawType();
// 用于这个泛型上中包含了内部类的情况,一般返回null
Type getOwnerType();
}
所以,知道了这里需要的泛型是怎么回事,一切都好说了,下面我们来完成之前留下的空方法。
1、实现一个简易的 ParameterizedType
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;}
}
2、生成Gson需要的泛型
2.1解析data是object的情况
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
return GSON.fromJson(reader, type);
}
2.2解析data是array的情况
是Array的情况要比是Object的情况多那么一步。
public static <T> Result<List<T>> fromJsonArray(Reader 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 GSON.fromJson(reader, type);
}
本次代码较少,不提供源码
虽然这篇博客是以Gson为例,但从上面的内容可以看出实际上和Gson关系不大,主要的内容还是Java的泛型基础,所以这种封装的方法同样适用于其它的框架。
最后借这次机会给安利一个简易的泛型生成库 TypeBuilder ,其最初实现的目的就是让大家快速的生成泛型信息,同时也会作一些参数检查,保证正确性。
用上面的代码给大家举个例子
public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz) {
Type type = TypeBuilder
.newInstance(Result.class)
.beginSubType(List.class)
.addTypeParam(clazz)
.endSubType()
.build();
return GSON.fromJson(reader, type);
}
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
Type type = TypeBuilder
.newInstance(Result.class)
.addTypeParam(clazz)
.build();
return GSON.fromJson(reader, type);
}
网友评论
TypeBuilder.newInstance(PageBean.class). beginSubType(List.class).beginSubType(Map.class). addTypeParam(String.class). addTypeParam(Info.class).endSubType().endSubType().build()
应该是这样
PageBean<List<Map<String, Info>>>这种类似的复杂类型呢
User{
name,sex;
}
既然这样,我们可以
// data 为 object 的情况--->UserResult.class
{"code":"0","message":"success","data":{“name”:"小明",“sex”:"男"}}
GSON.fromJson(reader, UserResult.class);
public class Result<T> {
public int code;
public String message;
public T data;
}
package com.ueuo.gson;
import com.google.gson.annotations.SerializedName;
public class User {
public String name;
public int age;
@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;
public User(){
}
public User(String name, int age){
this.name = name;
this.age = age;
}
public User(String name, int age, String emailAddress){
this.name = name;
this.age = age;
this.emailAddress = emailAddress;
}
}
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.Gson;
public class GsonGenericEnclosure {
static Gson GSON = new Gson();
public static void main(String[] args) {
System.out.println("搞定Gson泛型封装");
System.out.println("http://www.jianshu.com/p/d62c2be60617";);
System.out.println("作者: @怪盗kidou\n");
System.out.println("示例<T> Result<T> fromJsonObject(Reader reader, Class<T> clazz)");
String jsonstring = "{\"code\":\"123\",\"message\":\"msg\",\"data\":{\"name\":\"怪盗kidou\",\"age\":24,\"emailAddress\":\"ikidou_1@example.com\",\"email\":\"ikidou_2@example.com\",\"email_address\":\"ikidou_3@example.com\"}}";
Result<User> r = fromJsonObject(new StringReader(jsonstring), User.class);
System.out.println(r.data.name + "; " + r.data.age + "; " + r.data.emailAddress);
System.out.println("\n示例<T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz)");
String jsonlist = "{\"code\":\"123\",\"message\":\"msg\",\"data\":[{\"name\":\"怪盗kidou\",\"age\":24,\"emailAddress\":\"ikidou_1@example.com\",\"email\":\"ikidou_2@example.com\",\"email_address\":\"ikidou_3@example.com\"}]}";
Result<List<User>> r2 = fromJsonArray(new StringReader(jsonlist), User.class);
List<User> lists = r2.data;
for (User u : lists) {
System.out.println(u.name + "; " + u.age + "; " + u.emailAddress);
}
}
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
Type type = new ParameterizedTypeImpl(Result.class, new Class[] { clazz });
return GSON.fromJson(reader, type);
}
public static <T> Result<List<T>> fromJsonArray(Reader 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 GSON.fromJson(reader, type);
}
}
-dontwarn ikidou.reflect.**
-keep class ikidou.reflect.** { *; }
避免jar混淆过后,用这个 Type type = TypeBuilder
.newInstance(Result.class)
.addTypeParam(clazz)
.build();
Log.e("maxMemory","type -- " + type);
return new Gson().fromJson(readerJson, type);
解析不出来,但是能拿到Result这个,通过这个去获取对应的实体类,是空的,我不知道这个应该怎么混淆才不会出错,忘楼主提供解决方案啊
//得到list<t>
Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clas});
//得到backresult<list<T>>
Type type = new ParameterizedTypeImpl(BackResult.class, new Type[]{listType});
BackResult<List<T>> backResult = new Gson().fromJson(jsonObject, type);
return backResult;
}
请教楼主,这样写为什么有报错呢,报错为IllegalArgumentException: Expected a Class, ParameterizedType, or GenericArrayType, but ParameterizedTypeImpl is of type ParameterizedTypeImpl
源码解析:http://tinyurl.com/ycqn86hj
垒
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
return GSON.fromJson(reader, type);
}
这里面的Result还可以再封装吗?我想做个util直接传入参数就可以了返回一个been
比如 :
public static <T> T getObject(String jsonData, Class<T> type) {
T result = GsonInstance.getGson().fromJson(jsonData, type);
return result;
}
该怎么做呢?
直接用jdk的就好…
public class JsonConvert<T> {
private String mDataName = null;
public T parseData(String result) {
return fromJsonArray(result, T);
}
}
{"code":"0","message":"success","data":{}}
// data 为 array 的情况
{"code":"1","message":"fail","data":[]}
楼主,遇到这种情况怎么办,返回数据这样的,成功data是object,失败data是空的array,求解
List<T> list = new ArrayList<>();
JSONObject rootJsonObject = null;
try {
rootJsonObject = new JSONObject(result);
if (rootJsonObject.getBoolean(successKey)) {
JSONArray rootJsonArray = rootJsonObject.getJSONArray(arrKey);
Gson g = new Gson();
for (int i = 0; i < rootJsonArray.length(); i++) {
T t = g.fromJson(rootJsonArray.getJSONObject(i).toString(), clazz);
list.add(t);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return list;
}
大神觉得本人的这种做法怎样
现在服务器返回的数据格式是这样的
{"res":"1","data":{}}
当res为1时,data是一个jsonobj
res是其他错误值时,data有可能为空…
那么解析时…若data为空,就会报错,想问一下怎么指定空值解析为Null呢
服务器返回
{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}
我定义的实体是
private int code
private string message
private T data;
问题一 : 拿到服务器返回的东东,要判断到底是{} 还是 [] ? 然后调用楼主提供的方法?
问题二: 楼主最后举例那个是什么意思,没看懂
public int code;
public String message;
public T data;
}的类吗?