美文网首页
Gson解析类型A时结果是类型B的从而抛出JsonParseEx

Gson解析类型A时结果是类型B的从而抛出JsonParseEx

作者: zbmzly | 来源:发表于2019-09-29 15:53 被阅读0次

    改编自:
    https://codeday.me/bug/20190220/651618.html

    我正在使用Retrofit(与OkHttp和GSON组合)与在线网络服务进行通信.
    webservice包含了所有响应的默认包装,类似于:

    {
      "resultCode":"OK",
      "resultObj":"Can be a string or JSON object / array",
      "error":"",
      "message":""
    }
    

    在此示例中,resultCode将为OK或NO.此外,当处理请求时发生错误时,错误和消息只有任何内容.最后,但并非最不重要的是,resultObj将包含调用的实际结果(该示例中是一个字符串,但有些调用返回一个JSON数组或一个JSON对象).

    要处理这个元数据,我创建了一个泛型类,像这样:

    public class ApiResult<T> {
    
        private String error;
        private String message;
        private String resultCode;
        private T resultObj;
    
        // + some getters, setters etcetera
    }
    

    我还创建了代表有时在resultObj中给出的响应的类,并且我已经定义了一个用于Retrofit的接口,看起来有点像这样:

    public interface SomeWebService {
    
        @GET("/method/string")
        ApiResult<String> someCallThatReturnsAString();
    
        @GET("/method/pojo")
        ApiResult<SomeMappedResult> someCallThatReturnsAnObject();
    
    }
    

    只要请求有效,这一切都正常.但是当服务器端发生错误时,它仍然会返回一个String类型的resultObj.这会导致SomeCallThatReturnsAnObject在Retrofit RestAdapter / GSON库中崩溃,并显示如下消息:

    retrofit.RetrofitError:
    com.google.gson.JsonSyntaxException:
    java.lang.IllegalStateException:
    
    Expected BEGIN_OBJECT but was STRING at line 1 column 110 path $.resultObj
    

    现在,最后我的问题是:

    有没有(容易的)告诉GSON,如果它不符合预期的类型,它应该忽略(也就是“取消”)一个属性?
    我可以告诉GSON将空字符串视为null吗?

    答案:

    像这样定义你的模型:

    public class ApiResult {
    
        private String error;
        private String message;
        private String resultCode;
        private MyResultObject resultObj;
    }
    

    然后,为MyResultObject创建一个TypeAdapterFactory:

        /**
         * https://codeday.me/bug/20190220/651618.html
         * */
        public static class FindStringAndConvertToNullAdapterFactory implements TypeAdapterFactory {
            @Override
            @SuppressWarnings("unchecked")
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
                if (type.getRawType() == MyResultObject.class) {
                    TypeAdapter<MyResultObject> defaultAdapter = (TypeAdapter<MyResultObject>) gson.getDelegateAdapter(this, type);
                    return (TypeAdapter<T>) new FindStringAndConvertToNullAdapter(defaultAdapter);
                } else {
                    return null;
                }
            }
    
            public class FindStringAndConvertToNullAdapter<T> extends TypeAdapter<T> {
                protected TypeAdapter<T> defaultAdapter;
                public FindStringAndConvertToNullAdapter(TypeAdapter<T> defaultAdapter) {
                    this.defaultAdapter = defaultAdapter;
                }
                @Override
                public void write(JsonWriter out, T value) throws IOException {
                    defaultAdapter.write(out, value);
                }
                @Override
                public T read(JsonReader in) throws IOException {
                /*
                This is the critical part. So if the value is a string,
                Skip it (no exception) and return null.
                */
                    if (in.peek() == JsonToken.STRING) {
                        in.skipValue();
                        return null;
                    }
                    return defaultAdapter.read(in);
                }
            }
        }
    

    最后,为Gson注册MyResultObjectAdapterFactory:

         gson  = new GsonBuilder()
                   .registerTypeAdapterFactory(new FindStringAndConvertToNullAdapterFactory())
                   .create();
    

    现在,当使用该Gson对象反序列化一个ApiResult json时,如果它是一个字符串,那么resultObj将被设置为null.

    我希望这能解决你的问题=)

    相关文章

      网友评论

          本文标题:Gson解析类型A时结果是类型B的从而抛出JsonParseEx

          本文链接:https://www.haomeiwen.com/subject/zhssyctx.html