Retrofit使用チュートリアル:RetrofitでGSONは同じフィールドの異なるタイプのjsonデータを解析します

5514 ワード

前言
本文は主にプロジェクトでjsonを処理する実際の応用である.
ダイレクトコード
データを解析するときに、同じフィールドと異なるデータ型の問題が発生する可能性があります.次のデータを例に挙げます.
//        
{
    "retMsg": "success",
    "retData": {
        "city": "  ",
        "phone": "13282191888",
        "prefix": "1328219",
        "province": "  ",
        "supplier": "  "
    },
    "errNum": 0
}

//        
{
    "errNum": -1,
    "retMsg": "[132821918888]\u6b64\u53f7\u7801\u4e0d\u662f\u5408\u6cd5\u7684\u624b\u673a\u53f7!",
    "retData": []
}

返されたデータからretDataのタイプが2種類(オブジェクトと配列)になっていることがわかり、オブジェクトが返されて失敗したときにGSONが解析できず異常を放出するように定義されている場合.ここでGSONはRetrofitを組み合わせて使用しているので、解析に失敗した場合、異常はSubscriberの次の方法にコールバックされます.
public void onError(Throwable e) {}

このonError法では,他の有用なデータを得ることはできない.例えば、エラーメッセージを取得してユーザーに提示したいのですが、ここでは仕方がありません.
  • この問題を解決する方法は2つある:
  • 逆シーケンス化解析データ
  • を用いる.
  • データを異常放出し、処理
  • を行う.
  • の逆シーケンス化にはいくつかの限界があり、ここでretMsgは未知の汎用型を定義してもよく解決できない.だから以下の方法で異常データを放出します.

  • 一、Retrofitを初期化する際にカスタムブロッキングを使用する.
    Retrofit mAdapter = new Retrofit.Builder()
            .baseUrl(SERVER)
            //        
            .addConverterFactory(GsonDConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .client(getBuilder().build())
            .build();
    

    二、カスタムresponseBodyが返したデータを解析する.
    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);
        }
    }
    

    三、code値に基づいて異なるクラス解析データを選択する.
    convertメソッドでは,返すデータを具体的な汎用解析するように指定しない.code値で今回のリクエストを判定:成功したらtypeタイプの解析データを返し,失敗したら我々があらかじめ定義したHttpErrResultオブジェクトを用いて解析し,ResultExceptionでエラー情報を返す.
    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;
        }
    
        /**
         *         、          
         */
        @Override public T convert(ResponseBody value) throws IOException {
            String response = value.string();
            try {
                //    type      HttpResult  PhoneBean  retData      。
                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メソッドの解析失敗と成功情報.
    我々の以前の異常はonErrorメソッドにコールバックし,ResultException異常であるか否かを判断することにより,異常情報を取り出してPresenter処理にコールバックする.
    public abstract class HttpResultCallBack < M > extends Subscriber < HttpResult < M >> {
    
        /**
         *     
         */
        public abstract void onResponse(M m, int status);
        public abstract void onErr(String msg, int status);
    
        /**
         *     
         */
        @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("    ,     ", GlobalVar.RESULT_UNLOGIN);
                    Log.d("HttpManager", "    ==:" + e.getMessage());
                }
            }
            onCompleted();
        }
    
        /**
         * Http    
         */
        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);
            }
        }
    }
    

    githubソースダウンロード