序列化接口列表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按照预期反序列化了两个示例数据结构。 Container
和Animal
实现类只需要第二个例子的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; } }