GSON ignore elements with wrong type
Solution 1
Define your model like this:
public class ApiResult {
private String error;
private String message;
private String resultCode;
private MyResultObject resultObj;
}
Then, create a TypeAdapterFactory for MyResultObject:
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);
}
}
}
Finally, register MyResultObjectAdapterFactory for Gson:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyResultObjectAdapterFactory())
.create();
Now, when deserializing an ApiResult json with that Gson object, resultObj will be set null if it is a string.
I Hope this solves your problem =)
Solution 2
I've had a similar problem and came up with the following solution in the end:
In stead of trying to parse your element into a String
or Array
, try storing the data to a simple java.lang.Object
This prevents the parsing from crashing or throwing an exception.
eg. with GSON annotations the property of your model would look like this:
@SerializedName("resultObj")
@Expose
private java.lang.Object resultObj;
Next, when accessing your data at runtime, you can check if your resultObj
property is an instance of String
or not.
if(apiResultObject instanceof String ){
//Cast to string and do stuff
} else{
//Cast to array and do stuff
}
Original post: https://stackoverflow.com/a/34178082/3708094
Solution 3
First, this is a bad API design that you're dealing with. :-(
You can use a custom JsonDeserializer to handle this case.
Register it with 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();
Solution 4
I'm reusing my reponse pojo.
In one response it's String
and another response it's List<MajicalModel> magicalField
so one parsing failed.
I change it to com.google.gson.JsonElement magicalField;
it's work for me. This way it parse raw json and also ignore type mismatch.
Comments
-
Arno Moonen almost 2 years
I'm using Retrofit (in combination with OkHttp and GSON) to communicate with an online webservice. The webservice has a default wrapper around all it's responses, similar to:
{ "resultCode":"OK", "resultObj":"Can be a string or JSON object / array", "error":"", "message":"" }
In this example
resultCode
will either beOK
orNO
. Furthermoreerror
andmessage
only have any contents when an error has occured while processing the request. And last, but not least,resultObj
will contain the actual result from the call (which is a string in the example, but some calls return a JSON array or a JSON object).To process this meta data, I created a generic class, like this one:
public class ApiResult<T> { private String error; private String message; private String resultCode; private T resultObj; // + some getters, setters etcetera }
I've also created classes that represent the responses sometimes given in
resultObj
and I've defined an interface for use with Retrofit, that looks a bit like this:public interface SomeWebService { @GET("/method/string") ApiResult<String> someCallThatReturnsAString(); @GET("/method/pojo") ApiResult<SomeMappedResult> someCallThatReturnsAnObject(); }
As long as the request is valid this all works fine. But when an error occurs on the server side, it will still return a
resultObj
with a String-type. This causessomeCallThatReturnsAnObject
to crash inside the Retrofit RestAdapter / GSON library, with a message like this:retrofit.RetrofitError: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was STRING at line 1 column 110 path $.resultObj
Now, finally, my questions are:
- Is there an (easy) way to tell GSON that it should just ignore (aka "nullify") a property if it does not match the expected type?
- Can I tell GSON to treat empty strings as null?