Retrofit中使用Gson问题
问题
对于后台返回Json格式不严谨的情况,着实让人头疼。对于洁癖者使用Retrofit来说,更甚。
比如后台返回的成功和失败信息分别是:
失败:
{
"msg":"username or password is null",
"data":"",
"errno":1}
成功:
{"msg":"success",
"data":"{}",
"errno":0}
严谨的说,即使失败,data也需要返回一个内容为空的对象。但是由于老项目已经上线,前端不得不进行适应性的修改。
解决方案
1.将返回的信息以字符串形式进行接收,通过JSONObject拿到msg,判断errno是否为0,从而进行解析与否data字段的数据。
2.通过Gson中对特有类型进行特殊的处理。
第一种方案明显的缺点是:对于业务逻辑有很强的依赖,并且每次请求相同的返回类型时,都需要有判断逻辑。
第二种方案就很有灵性:Gson给我们提供了对特殊类型进行处理的方法,只需要对使用到Gson的地方进行自定义GsonBuilder,注册一下解析的类型,以及JsonDeserializer的子类即可。
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(UnOfficialTime.class,new UnOfficialTimeDeserializer());
Gson声明特殊处理类
直接上关键代码
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(UnOfficialTime.class,new UnOfficialTimeDeserializer());
gsonBuilder.registerTypeAdapter(new TypeToken<ImplResponse<LoginResponse>>(){}.getType(),new ImplResponseDeserializer<LoginResponse>(LoginResponse.class));
ImplResponse的作用是抽取共有的字段,对data进行泛型处理
ImplResponse.java
public class ImplResponse<T> extends BaseResponse{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "ImplResponse{" +
"data=" + data +
","+super.toString()+
'}';
}
}
BaseResponse.java
public class BaseResponse {
private String msg;
private String errno;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getErrno() {
return errno;
}
public void setErrno(String errno) {
this.errno = errno;
}
@Override
public String toString() {
return
"msg='" + msg + '\'' +
", errno='" + errno + '\''
;
}
}
一般的使用方法是,对每个Response进行特殊的声明,类似UnOfficialTime类的声明。但是对于ImplResponse这种带有泛型声明的类,共有层的处理就比较繁琐一些了。因为使用泛型,对于Gson.fromGson(json,Type.class)中Type不支持泛型,TypeToken也仅仅是支持明确的泛型,所以这里就需要稍微改变一下实现,从ImplResponseDeserializer的构造方法中进行传递Gson.fromJson中所需要的class。
public class ImplResponseDeserializer<T> implements JsonDeserializer<ImplResponse<T>> {
private final Class clazz;
public ImplResponseDeserializer(Class clazz){
this.clazz = clazz;
}
@Override
public ImplResponse<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
ImplResponse<T> objectImplResponse = new ImplResponse<>();
String msg = "";
T data = null;
if (json.isJsonObject()) {
JsonObject obj = json.getAsJsonObject();
if (!obj.get("msg").isJsonNull()) {
msg = obj.get("msg").getAsString();
}
objectImplResponse.setMsg(msg);
if(obj.get("data").isJsonObject()){
String data1 = obj.get("data").getAsJsonObject().toString();
data = (T) new Gson().fromJson(data1, clazz);
}
objectImplResponse.setData(data);
}
return objectImplResponse;
}
}
接着进行封装GsonUtils
/**
* Author :feiqing
* Created on 2017/11/15
* Motify on 2017/11/15
* Version : 1.0
* Description :Gson工具
*/
public class GsonUtils {
private static Gson instance;
private static Gson pureInstance;
/**
* 获取一个针对特定对象进行加工判断的Gson
* @return
*/
public static Gson getGson(){
if(instance==null){
synchronized (GsonUtils.class){
if(instance==null){
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(UnOfficialTime.class,new UnOfficialTimeDeserializer());
gsonBuilder.registerTypeAdapter(new TypeToken<ImplResponse<LoginResponse>>(){}.getType(),new ImplResponseDeserializer<LoginResponse>(LoginResponse.class));
}
}
}
return instance;
}
}
接下来植入到Retrofit中
retrofit = new Retrofit.Builder()
.baseUrl(MyConstant.HTTP_BASEURL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(GsonUtils.getGson()))
.build();
至此,大功告成。