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?
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
inReflectiveTypeAdapterFactory
So the instance creator is more suitable if the type you told gson to deserialize has no zero arg constructor for gson to call.