序列化接口列表GSON

我在GSON中遇到了一些奇怪的行为。

如果我有以下类结构:

public interface Animal { public void nothing(); } public class Cat implements Animal { private String name; public Cat(String name) { super(); this.name = name; } public Cat(){} @Override public void nothing() { // TODO Auto-generated method stub }; } public class Dog implements Animal { private String name; public Dog(String name) { super(); this.name = name; } public Dog(){} @Override public void nothing() { // TODO Auto-generated method stub }; } 

我可以做这个:

 ArrayList animals = new ArrayList(); animals.add(new Cat("Betty")); animals.add(new Dog("Fred")); System.out.println(gson.toJson(animals)); 

得到这个输出:

 [{"name":"Betty"},{"name":"Fred"}] 

但是,如果我把animals放入一个含有的类:

 public class Container { List animals = new ArrayList(); public void addAnimal(Animal a){ animals.add(a); } } 

并致电:

 Container container = new Container(); container.addAnimal(new Cat("betty")); System.out.println(gson.toJson(container)); 

我明白了:

 {"animals":[{}]} 

看起来GSON可以在该列表本身时序列化接口List列表,但是当列表包含在另一个类中时,GSON会出现问题。

知道我做错了什么吗?

作为旁注,我可以使用自定义反序列化器正确地将json字符串反序列化为正确的类型。 这是序列化给我带来的问题。

谢谢

它远非漂亮,但我现在使用的解决方案是使用

 JsonObject jsonObject = gson.toJsonTree(container).getAsJsonObject(); 

构建一个JsonObject。 然后我打电话给:

 jsonObject.remove("animals"); jsonObject.add("animals",gson.toJsonTree(container.getAnimals())); 

和waa laa,正确的jsonforms的对象。

加分点:我有一个嵌套容器列表,所以我必须构造一个JsonArray,以便我可以遍历我的容器并在每个容器上调用我的自定义toJson()。

故事的道德:使用。添加界面列表

 jsonObject.remove(); jsonObject.add(propertyName, property); 

使用JsonArray处理容器和迭代容器列表(在列表中只使用toJson()不会在子容器上调用您的特殊方法)。

绝对还在寻找更自然的解决方案。

快乐的编码

只需使用一个显式获取对象类的自定义JsonSerializer就足够了(可能由于某些原因,可能是Gson获取声明的类型)。 这是序列化接口的一般解决方案:

 public static class InterfaceSerializer implements JsonSerializer { 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()); } } 

以您为例,我可以执行以下操作:

 GsonBuilder gbuild = new GsonBuilder(); Gson standard = gbuild.create(); ArrayList 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)); 

这输出:

 [{"name":"Betty","hates":"Everything"},{"name":"Fred","loves":"Everything"}] {"animals":[{},{}]} {"animals":[{"name":"Betty","hates":"Everything"}, "name":"Fred","loves":"Everything"}]} 

最后一项是正确序列化的字符串。

不幸的是,这不足以反序列化JSON,因为生成的JSON不包含任何类型信息。 查看如何使用接口序列化类的答案? 用于跟踪对象类型的方法,因此序列化/反序列化对象。

Gson的最新版本仍然如原始问题中所述。 (我想我会记录一个增强请求,如果还没有。)

对于它的价值, jackson按照预期反序列化了两个示例数据结构。 ContainerAnimal实现类只需要第二个例子的getter。

 // output: {"animals":[{"name":"betty"},{"name":"fred"}]} import java.io.StringWriter; import java.util.ArrayList; import org.codehaus.jackson.map.ObjectMapper; public class JacksonFoo { public static void main(String[] args) throws Exception { Container container = new Container(); container.addAnimal(new Cat("betty")); container.addAnimal(new Dog("fred")); ObjectMapper mapper = new ObjectMapper(); StringWriter writer = new StringWriter(); mapper.writeValue(writer, container); System.out.println(writer); } } class Container { ArrayList animals = new ArrayList(); public void addAnimal(Animal a) { animals.add(a); } public ArrayList getAnimals() { return animals; } } interface Animal { public void nothing(); } class Cat implements Animal { private String name; public Cat(String name) { this.name = name; } public Cat() { } @Override public void nothing() { } public String getName() { return name; } } class Dog implements Animal { private String name; public Dog(String name) { this.name = name; } public Dog() { } @Override public void nothing() { } public String getName() { return name; } }