Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an easy way to make Gson skip a field if there's an error deserializing it?

I'm trying to deserialize some data with Gson (Java) and the API I'm pulling data from sometimes has data of the wrong type in a field. I.e. if I'm expecting an array of String types, it might encounter a Boolean.

Now I realize these are my current options:

  • Always ignore the field from deserialization
  • Create a custom TypeAdapter to do the deserialization and the catch the error and do something (like set the field to null)

However I'm asking if there's another way to easily make it so if there's an exception parsing a certain field, that Gson will just ignore that field. Something like an annotation on that field like @Skippable or maybe a setting when using the GsonBuilder to create a Gson object?

Is anyone familiar with such a thing?

like image 820
Ryan Stull Avatar asked Jan 08 '20 23:01

Ryan Stull


1 Answers

It is not an easy task to handle properly all possible errors in JSON and mismatches between payload and POJO model. But we can try to implement com.google.gson.TypeAdapterFactory interface and wrap all default TypeAdapters in try-catch and skip invalid data. Example solution could look like this:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Gson gson = new GsonBuilder()
                .setLenient()
                .registerTypeAdapterFactory(new IgnoreFailureTypeAdapterFactory())
                .create();

        Entity entries = gson.fromJson(new FileReader(jsonFile), Entity.class);
        System.out.println(entries);
    }

}

class IgnoreFailureTypeAdapterFactory implements TypeAdapterFactory {

    public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        return createCustomTypeAdapter(delegate);
    }

    private <T> TypeAdapter<T> createCustomTypeAdapter(TypeAdapter<T> delegate) {
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                try {
                    return delegate.read(in);
                } catch (Exception e) {
                    in.skipValue();
                    return null;
                }
            }
        };
    }
}

class Entity {
    private Integer id;
    private String name;

    // getters, setters, toString
}

For example, above code prints:

Entity{id=null, name='1'}

for below JSON payload:

{
  "id": [
    {
      "a": "A"
    }
  ],
  "name": 1
}

See also:

  • Same object referenced in two classes, duplicated instance after decode
like image 163
Michał Ziober Avatar answered Sep 29 '22 06:09

Michał Ziober