It's enough to simply use a custom JsonSerializer that explicitly gets the class of the object (presumably Gson's getting the declared type instead, for some reason). Here's a general solution for serializing Interfaces:
public static class InterfaceSerializer<T> implements JsonSerializer<T> {
public JsonElement serialize(T link, Type type,
JsonSerializationContext context) {
// Odd Gson quirk
// not smart enough to use the actual type rather than the interface
return context.serialize(link, link.getClass());
}
}
For your example, I can do the following:
GsonBuilder gbuild = new GsonBuilder();
Gson standard = gbuild.create();
ArrayList<Animal> animals = Lists.newArrayList(new Cat("Betty"),new Dog("Fred"));
System.out.println(standard.toJson(animals));
Container c = new Container();
c.animals.addAll(animals);
System.out.println(standard.toJson(c));
Gson interfaceAware = gbuild
.registerTypeAdapter(Animal.class, new InterfaceSerializer<>()).create();
System.out.println(interfaceAware.toJson(c));
This outputs:
[{"name":"Betty","hates":"Everything"},{"name":"Fred","loves":"Everything"}]
{"animals":[{},{}]}
{"animals":[{"name":"Betty","hates":"Everything"}, "name":"Fred","loves":"Everything"}]}
The last item being the correctly serialized string.
This isn't enough to deserialize the JSON, unfortunately, because the resulting JSON doesn't contain any type information. Check out this answer to How to serialize a class with an interface? for a way to track the object's type, and therefore serialize/deserialize the object.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…