前言
本文主要是對(duì)在項(xiàng)目中處理json的實(shí)際應(yīng)用辉懒。
直接上代碼
解析數(shù)據(jù)的時(shí)候有可能會(huì)遇到同字段不同數(shù)據(jù)類(lèi)型的問(wèn)題阳惹,以下面數(shù)據(jù)為例:
// 返回成功的數(shù)據(jù)
{
"retMsg": "success",
"retData": {
"city": "杭州",
"phone": "13282191888",
"prefix": "1328219",
"province": "浙江",
"supplier": "聯(lián)通"
},
"errNum": 0
}
// 返回失敗的數(shù)據(jù)
{
"errNum": -1,
"retMsg": "[132821918888]\u6b64\u53f7\u7801\u4e0d\u662f\u5408\u6cd5\u7684\u624b\u673a\u53f7!",
"retData": []
}
從返回的數(shù)據(jù)可以看出retData的類(lèi)型變成了兩種(對(duì)象和數(shù)組),如果只定義成對(duì)象當(dāng)返回失敗的時(shí)候GSON無(wú)法解析并拋出異常眶俩。因?yàn)槲疫@里GSON是組合Retrofit使用的莹汤,當(dāng)解析失敗異常會(huì)回調(diào)到Subscriber中的以下方法里:
public void onError(Throwable e) {}
在這個(gè)onError方法里,我們無(wú)法獲取到其它有用的數(shù)據(jù)颠印。比如說(shuō)我們想獲取錯(cuò)誤信息提示給用戶纲岭,但在這里是沒(méi)有辦法的。
- 解決這個(gè)問(wèn)題的方法有兩種:
- 使用反序列化解析數(shù)據(jù)
- 把數(shù)據(jù)通過(guò)異常拋出线罕,并做處理
- 反序列化有一些局限性止潮,這里的retMsg定義的是未知的泛型并不能很好的解決。所以以下的方法我使用拋出異常數(shù)據(jù)钞楼。
一喇闸、初始化Retrofit時(shí)使用自定義攔截器。
Retrofit mAdapter = new Retrofit.Builder()
.baseUrl(SERVER)
// 增加自定義解析
.addConverterFactory(GsonDConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getBuilder().build())
.build();
二窿凤、自定義responseBody對(duì)返回的數(shù)據(jù)解析仅偎。
public final class GsonDConverterFactory extends Converter.Factory {
public static GsonDConverterFactory create() {
return create(new Gson());
}
public static GsonDConverterFactory create(Gson gson) {
return new GsonDConverterFactory(gson);
}
private final Gson gson;
private GsonDConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override public Converter < ResponseBody,
?>responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new GsonResponseBodyConverter < >(gson, type);
}
@Override public Converter < ?,
RequestBody > requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter < ?>adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter < >(gson, adapter);
}
}
三跨蟹、根據(jù)code值選擇不同的類(lèi)解析數(shù)據(jù)雳殊。
在convert方法里,先不指定具體泛型解析出返回的數(shù)據(jù)窗轩。通過(guò)code值判斷本次請(qǐng)求:成功就返回type類(lèi)型的解析數(shù)據(jù)夯秃,失敗就使用我們提前定義好的HttpErrResult對(duì)象進(jìn)行解析,并通過(guò)ResultException把錯(cuò)誤信息返回回去痢艺。
final class GsonResponseBodyConverter < T > implements Converter < ResponseBody,
T > {
private final Gson gson;
private final Type type;
GsonResponseBodyConverter(Gson gson, Type type) {
this.gson = gson;
this.type = type;
}
/**
* 針對(duì)數(shù)據(jù)返回成功仓洼、錯(cuò)誤不同類(lèi)型字段處理
*/
@Override public T convert(ResponseBody value) throws IOException {
String response = value.string();
try {
// 這里的type實(shí)際類(lèi)型是 HttpResult<PhoneBean> PhoneBean就是retData要獲取的對(duì)象。
HttpResult result = gson.fromJson(response, HttpResult.class);
int code = result.getErrNum();
if (code == 0) {
return gson.fromJson(response, type);
} else {
Log.d("HttpManager", "返回err==:" + response);
HttpErrResult errResponse = gson.fromJson(response, HttpErrResult.class);
if (code == -1) {
throw new ResultException(errResponse.getRetMsg(), code);
} else {
throw new ResultException(errResponse.getErrMsg(), code);
}
}
} finally {
value.close();
}
}
}
四堤舒、自定義Retrofit的Subscriber方法解析失敗與成功信息色建。
我們之前的異常會(huì)回調(diào)到onError方法里,通過(guò)判斷是否是ResultException異常舌缤,取出異常信息回調(diào)給Presenter處理箕戳。
public abstract class HttpResultCallBack < M > extends Subscriber < HttpResult < M >> {
/**
* 請(qǐng)求返回
*/
public abstract void onResponse(M m, int status);
public abstract void onErr(String msg, int status);
/**
* 請(qǐng)求完成
*/
@Override public void onCompleted() {}
@Override public void onError(Throwable e) {
if (e != null) {
if (e instanceof ResultException) {
ResultException err = (ResultException) e;
onErr(err.getErrMsg(), GlobalVar.RESULT_UNLOGIN);
} else {
onErr("網(wǎng)絡(luò)異常,請(qǐng)檢查網(wǎng)絡(luò)", GlobalVar.RESULT_UNLOGIN);
Log.d("HttpManager", "解析失敗==:" + e.getMessage());
}
}
onCompleted();
}
/**
* Http請(qǐng)求失敗
*/
private void onHttpFail(String msg, int status) {
onErr(msg, status);
}
@Override public void onNext(HttpResult < M > result) {
String jsonResponse = new Gson().toJson(result);
Log.d("HttpManager", "返回ok==:" + jsonResponse);
if (result.getErrNum() == GlobalVar.RESULT_OK) {
onResponse(result.getRetData(), GlobalVar.RESULT_OK);
} else {
onHttpFail(result.getErrMsg(), GlobalVar.RESULT_UNLOGIN);
}
}
}