美文网首页
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