Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GSON ignore elements with wrong type

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 be OK or NO. Furthermore error and message 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 causes someCallThatReturnsAnObject 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:

  1. 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?
  2. Can I tell GSON to treat empty strings as null?
like image 513
Arno Moonen Avatar asked Nov 30 '14 11:11

Arno Moonen


People also ask

Does Gson ignore extra fields?

As you can see, Gson will ignore the unknown fields and simply match the fields that it's able to.

How do I ignore NULL values in Gson?

By default, the Gson object does not serialize the fields with null values to JSON. If a field in a Java object is null, Gson excludes it. We can force Gson to serialize null values via the GsonBuilder class.


1 Answers

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 =)

like image 72
Heikki Hautala Avatar answered Sep 19 '22 03:09

Heikki Hautala