GSON忽略错误类型的元素

时间:2014-11-30 11:12:06

标签: java android json gson retrofit

我使用Retrofit(与OkHttp和GSON结合使用)与在线网络服务进行通信。 Web服务有一个默认的包装器,包含所有响应,类似于:

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

在此示例中,resultCode将为OKNO。此外,errormessage在处理请求时发生错误时只有任何内容。最后,但并非最不重要的是,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:

     

预计BEGIN_OBJECT但是STRING在第1行第110列路径$ .resultObj

现在,最后,我的问题是:

  1. 是否有一种(简单)方法告诉GSON如果它与预期类型不匹配,它应该忽略(又名&#34;无效&#34;)属性?
  2. 我可以告诉GSON将空字符串视为空吗?

5 个答案:

答案 0 :(得分:20)

像这样定义你的模型:

public class ApiResult {

    private String error;
    private String message;
    private String resultCode;
    private MyResultObject resultObj;
}

然后,为 MyResultObject 创建 TypeAdapterFactory

public class MyResultObjectAdapterFactory implements TypeAdapterFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (type.getRawType()!= MyResultObject.class) return null;

        TypeAdapter<MyResultObject> defaultAdapter = (TypeAdapter<MyResultObject>) gson.getDelegateAdapter(this, type);
        return (TypeAdapter<T>) new MyResultObjectAdapter(defaultAdapter);
    }

    public class MyResultObjectAdapter extends TypeAdapter<MyResultObject> {

        protected TypeAdapter<MyResultObject> defaultAdapter;


        public MyResultObjectAdapter(TypeAdapter<MyResultObject> defaultAdapter) {
            this.defaultAdapter = defaultAdapter;
        }

        @Override
        public void write(JsonWriter out, MyResultObject value) throws IOException {
            defaultAdapter.write(out, value);
        }

        @Override
        public MyResultObject 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 gson = new GsonBuilder()
    .registerTypeAdapterFactory(new MyResultObjectAdapterFactory())
    .create();

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

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

答案 1 :(得分:6)

我遇到了类似的问题,最后提出了以下解决方案:

不要试图将您的元素解析为StringArray,而是尝试将数据存储到简单的java.lang.Object

这可以防止解析崩溃或抛出异常。

例如。使用GSON注释,模型的属性将如下所示:

@SerializedName("resultObj")
@Expose
private java.lang.Object resultObj;

接下来,在运行时访问您的数据时,您可以检查resultObj属性是否是String的实例。

if(apiResultObject instanceof String ){
    //Cast to string and do stuff

} else{
    //Cast to array and do stuff

}

原帖:https://stackoverflow.com/a/34178082/3708094

答案 2 :(得分:3)

首先,这是一个糟糕的API设计,你正在处理。 : - (

您可以使用自定义JsonDeserializer来处理此案例。

将其注册为Retrofit:

MyJsonDeserializer deserializer = new MyJsonDeserializer()).create();
final Gson gson = new GsonBuilder().registerTypeAdapter(ApiResult.class, deserializer);
RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint(API_URL)
    .setConverter(new GsonConverter(gson))
    .build();

答案 3 :(得分:1)

我正在重复使用我的响应pojo。

在一个回复中,String和另一个响应它List<MajicalModel> magicalField,因此一次解析失败。

我将其更改为com.google.gson.JsonElement magicalField;它为我工作。这样它就可以解析原始json并忽略类型不匹配。

答案 4 :(得分:0)

您也可以使用此代码:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
yourObject = objectMapper.readValue(jsonString, <ClassName>.class);