首頁>技術>

前言

接入某業務介面時,發現其成功和失敗狀態下,data欄位的資料型別並不一致。

介面已上線多時無法隨便改動,故需要客戶端自己相容

請求成功時,data是T型別(某自定義資料型別)

{	"code" : 0,	"data" : {}}

請求失敗時,data是String型別

{	"code" : 1000,	"data" : "錯誤資訊"}

一個data欄位對應了兩種資料型別,只能把它定義成Object型別。請求成功時,由於data定義成Object型別,預設情況下Gson會將其解析成LinkedTreeMap。使用時需要從map中用key來取值,極其不方便。

另闢蹊徑

既然將data定義成Object型別極其不方便,那麼data就只能定義成T型別了。但是這樣的話,請求失敗時解析又會丟擲異常(因為此時data為String型別)。

能否做到請求成功時,將data解析成T型別,而其它情況不解析data或者將data的值挪到別的欄位中(如message欄位)?個人更傾向於後者的想法。也就是能否將請求失敗時的JSON修改成以下格式?

{	"code" : 1000,	"data" : null,	"message" : "錯誤資訊"}
實現方案

基於上述思路,我想到了兩種做法

自定義OkHttp的Interceptor自定義Gson的TypeAdapter自定義Interceptor

簡單說下做法

只攔截某(幾)個介面從Response中讀取並解析JSON根據code判斷是否請求成功如果請求失敗則生成新的JSON,並返回新的Response

這種做法個人感覺使用比較繁瑣,而且需額外解析JSON。具體做法可以看下這篇文章Android 優雅地處理後臺返回的騷資料

自定義TypeAdapter

如何使用TypeAdapter修改JSON結構呢?經過一番搜尋之後,我在stackoverflow上找到了想要的答案。根據上述回答的實現,我稍微修改了一下

public class CustomizedTypeAdapterFactory<C> implements TypeAdapterFactory {    private final Class<C> mClass;    public CustomizedTypeAdapterFactory(Class<C> cls) {        this.mClass = cls;    }    @Override    public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {        //noinspection unchecked        return type.getRawType() == mClass                ? (TypeAdapter<T>) createAdapter(gson, (TypeToken<C>) type)                : null;    }    private TypeAdapter<C> createAdapter(Gson gson, TypeToken<C> type) {        // 獲取Gson中該型別對應的TypeAdapter        final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);        return new TypeAdapter<C>() {            @Override            public void write(JsonWriter out, C value) throws IOException {                // 將value轉換成JSON樹並將其序列化                JsonElement tree = delegate.toJsonTree(value);                onWrite(value, tree);                Streams.write(tree, out);            }            @Override            public C read(JsonReader in) throws IOException {                // 將讀取的JSON串反序列化JSON樹                JsonElement tree = Streams.parse(in);                onRead(tree);                return delegate.fromJsonTree(tree);            }        };    }    protected void onWrite(C value, JsonElement json) {    }    protected void onRead(JsonElement json) {    }}

繼承上述TypeAdapterFactory,重寫onWrite和onRead方法,就可以實現在序列化和反序列化之前修改JSON的的功能。

public class BaseResponse<T> {    private int code;    private T data;    private String message; // 新增欄位    ...}public class ResponseTypeAdapterFactory extends CustomizedTypeAdapterFactory<BaseResponse> {    private static final String CODE_OK = "0";    public static final String DATA = "data";    public static final String CODE = "code";    public static final String MSG = "message";    public ResponseTypeAdapterFactory() {        super(BaseResponse.class);    }    @Override    protected void onRead(JsonElement json) {        JsonObject jsonObject = json.getAsJsonObject();        JsonElement code = jsonObject.get(CODE);// 獲取code欄位        // 呼叫成功,不處理        if (code == null || !code.isJsonPrimitive() || CODE_OK.equals(code.getAsString())) {            return;        }        JsonElement data = jsonObject.get(DATA);        if (data != null) {            // 將data的值挪到message中            jsonObject.remove(DATA);            jsonObject.add(MSG, data);        }    }}

自定義一個ResponseTypeAdapterFactory,專門用於解析BaseResponse這種型別。因為只需要修改反序列化的JSON結構,所以只重寫了onRead方法。onRead方法的邏輯也很簡單。簡單說下就是獲取code欄位,判斷是否請求失敗,如果是則將data的值挪到message中。

最後,別忘記將ResponseTypeAdapterFactory註冊到Gson,還有配置混淆規則!!

	Gson gson = new GsonBuilder()	        .registerTypeAdapterFactory(new ResponseTypeAdapterFactory())	        .create();
總結

利用Gson的TypeAdapter,實現了根據code來修改JSON結構的功能,從而達到動態解析JSON的效果。使用本方案可以很好地解決JSON結構不一致的問題。題外話,遇到JSON結構不一致的問題,務必先與後臺同事溝通溝通再溝通!!!

4
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Linux環境安裝Redis