相关文章推荐
彷徨的仙人掌  ·  Java ...·  1 月前    · 
爱运动的小虾米  ·  解决方案 ...·  6 月前    · 
另类的墨镜  ·  实战总结 Vue ...·  10 月前    · 

一个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结构不一致的问题,务必先与后台同事沟通沟通再沟通!!!

  • Gson custom seralizer for one variable (of many) in an object using TypeAdapter
  • Android 优雅地处理后台返回的骚数据
  • 你真的会用Gson吗?Gson使用指南(四)
  • benio 19.7k
    粉丝