我使用Retrofit(与OkHttp和GSON结合使用)与在线网络服务进行通信。 Web服务有一个默认的包装器,包含所有响应,类似于:
{
"resultCode":"OK",
"resultObj":"Can be a string or JSON object / array",
"error":"",
"message":""
}
在此示例中,resultCode
将为OK
或NO
。此外,error
和message
在处理请求时发生错误时只有任何内容。最后,但并非最不重要的是,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
现在,最后,我的问题是:
答案 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)
我遇到了类似的问题,最后提出了以下解决方案:
不要试图将您的元素解析为String
或Array
,而是尝试将数据存储到简单的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
}
答案 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);