Fields not deserialized when instance creator is registered with gson

642 Views Asked by At

I'm using gson to deserialize objects, and I'm telling gson the interface class rather than the implementation class of the object I want. I register an InstanceCreator that constructs an instance of the implementation class.

This test demonstrates the problem that I encounter:

public class UnexpectedGsonBehaviorTest {

    public static interface Fruits {
        List<String> getFruitList();
    }

    public static class FruitsImpl implements Fruits {

        private List<String> fruitList;

        public List<String> getFruitList() {
            return fruitList;
        }

        public String toString() {
            return "FruitsImpl{" + "fruitList=" + fruitList + '}';
        }

    }

    @Test
    public void testUseCustomFruitsInstanceCreator() {
        Gson gson = new GsonBuilder()
          .registerTypeAdapter(Fruits.class, new InstanceCreator<Fruits>(){
            public Fruits createInstance(Type type) {
                return new FruitsImpl();
            }
        }).create();
        String fruitsJson = "{\"fruitList\": [\"apples\", \"pears\", \"bananas\"]}";
        System.out.println("input: " + fruitsJson);
        Fruits fruits = gson.fromJson(fruitsJson, Fruits.class);
        System.out.println("output: " + fruits);
        assertNotNull("expect non-null fruit list", fruits.getFruitList());
    }

}

I expect the list member of the FruitsImpl object to be deserialized, but it is not. The output is

input: {"fruitList": ["apples", "pears", "bananas"]}
output: FruitsImpl{fruitList=null} 

and the assertion fails.

If I just use a default gson instance and gson.fromJson(fruitsJson, FruitsImpl.class), the list field is correctly deserialized. Am I using gson wrong, or is there some other issue?

1

There are 1 best solutions below

1
On BEST ANSWER

If you debug it and put a breakpoint inside createInstance and step into the gson code you should see the problem.

After instantiation gson tries to populate all the bound fields in the ReflectiveTypeAdapterFactory. The bound fields map is empty in your case. This is because it can't inspect your interface for fields.

From getBoundFields in ReflectiveTypeAdapterFactory

Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) {
  return result;
}

So the instance creator is more suitable if the type you told gson to deserialize has no zero arg constructor for gson to call.