Gson Type Adapter与Custom Deseralizer

下面的示例显示了一个包含抽象类(Member)集合的类(Club)。 我很困惑我是否需要TypeAdapter或JsonDeserializer来使反序列化正常工作。 序列化在没有任何帮助的情况下工作正常,但反序列化会抛出exception。 为了说明我已经构建了以下“克隆”测试。 如果有人能展示一个有效的例子,我将非常感激。

第一级俱乐部

package gson.test; import java.util.ArrayList; import com.google.gson.Gson; public class Club { public static void main(String[] args) { // Setup a Club with 2 members Club myClub = new Club(); myClub.addMember(new Silver()); myClub.addMember(new Gold()); // Serialize to JSON Gson gson = new Gson(); String myJsonClub = gson.toJson(myClub); System.out.println(myJsonClub); // De-Serialize to Club Club myNewClub = gson.fromJson(myJsonClub, Club.class); System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed"); } private String title = "MyClub"; private ArrayList members = new ArrayList(); public boolean equals(Club that) { if (!this.title.equals(that.title)) return false; for (int i=0; i<this.members.size(); i++) { if (! this.getMember(i).equals(that.getMember(i))) return false; } return true; } public void addMember(Member newMember) { members.add(newMember); } public Member getMember(int i) { return members.get(i); } } 

现在是抽象基类成员

 package gson.test; public abstract class Member { private int type; private String name = ""; public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean equals(Member that) {return this.name.equals(that.name);} } 

会员的两个具体子类(金银)

 package gson.test; public class Gold extends Member { private String goldData = "SomeGoldData"; public Gold() { super(); this.setType(2); } public boolean equals(Gold that) { return (super.equals(that) && this.goldData.equals(that.goldData)); } } package gson.test; public class Silver extends Member { private String silverData = "SomeSilverData"; public Silver() { super(); this.setType(1); } public boolean equals(Silver that) { return (super.equals(that) && this.silverData.equals(that.silverData)); } } 

最后输出

  {"title":"MyClub","members":[{"silverData":"SomeSilverData","type":1,"name":""},{"goldData":"SomeGoldData","type":2,"name":""}]} Exception in thread "main" java.lang.RuntimeException: Failed to invoke public gson.test.Member() with no args at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:107) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:186) ... 

你可以做到这两点。 您选择哪一个取决于潜在的性能影响,以及愿意编写多少代码。

反序列化器更昂贵。 这是因为反序列化器的输入是一个json树,并且GSon必须创建一个与你的类匹配的属性的完整JsonElement子树,然后才能将它传递给你的反序列化器。 如果你的课程有很多嵌套,那么这个成本会增加。 对于普通物体,它可以忽略不计。

您似乎将根据将包含在目标对象中的type属性的值知道要创建的类。 你的解串器需​​要

  • 查看传递的JsonElement对象,读取type属性,确定类型
  • 使用类和传递给您的相同元素调用context.deserialize()
  • 如果类型丢失或无效,则抛出错误

您的类型适配器必须更复杂。 类型适配器的输入是流,而不是元素/子树。 您可以完全从流中加载下一个值,解析它,然后完成反序列化器所做的操作,这没有意义,您可以使用反序列化器接口。 或者,您可以阅读流,查看有哪些属性,将它们保存到局部变量中,直到找到type属性(无法预测其位置),然后读完剩余的属性,然后创建最终属性基于类型的Gold / Silver对象,以及读取和保存的所有属性。

好的,真实的工作示例(我很确定这一次)。

该俱乐部

 package gson.test; import java.util.ArrayList; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class Club { public static void main(String[] args) { // Setup a Club with 2 members Club myClub = new Club(); myClub.addMember(new Silver("Jack")); myClub.addMember(new Gold("Jill")); myClub.addMember(new Silver("Mike")); // Get the GSON Object and register Type Adapter GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Member.class, new MemberDeserializer()); builder.registerTypeAdapter(Member.class, new MemberSerializer()); builder.setPrettyPrinting(); Gson gson = builder.create(); // Serialize Club to JSON String myJsonClub = gson.toJson(myClub); // De-Serialize to Club Club myNewClub = gson.fromJson(myJsonClub, Club.class); System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed"); System.out.println(gson.toJson(myNewClub)); } private String title = "MyClub"; private ArrayList members = new ArrayList(); public boolean equals(Object club) { Club that = (Club) club; if (!this.title.equals(that.title)) return false; for (int i=0; i 

会员抽象类

 package gson.test; public abstract class Member { private String clsname = this.getClass().getName() ; private int type; private String name = "unknown"; public Member() { } public Member(String theName) {this.name = theName;} public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean equals(Object member) { Member that = (Member) member; return this.name.equals(that.name); } } 

混凝土子类银和金

 package gson.test; public class Silver extends Member { private String silverData = "SomeSilverData"; public Silver() { super(); this.setType(1); } public Silver(String theName) { super(theName); this.setType(1); } public boolean equals(Object that) { Silver silver = (Silver)that; return (super.equals(that) && this.silverData.equals(silver.silverData)); } } package gson.test; public class Gold extends Member { private String goldData = "SomeGoldData"; private String extraData = "Extra Gold Data"; public Gold() { super(); this.setType(2); } public Gold(String theName) { super(theName); this.setType(2); } public boolean equals(Gold that) { Gold gold = (Gold) that; return (super.equals(that) && this.goldData.equals(gold.goldData)); } } 

定制会员Serailizer

 package gson.test; import java.lang.reflect.Type; import com.google.gson.JsonElement; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; public class MemberSerializer implements JsonSerializer { public JsonElement serialize(Member src, Type member, JsonSerializationContext context) { switch (src.getType()) { case 1: return context.serialize((Silver)src); case 2: return context.serialize((Gold)src); default: return null; } } } 

自定义反序列化器

 package gson.test; import java.lang.reflect.Type; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; public class MemberDeserializer implements JsonDeserializer { @Override public Member deserialize(JsonElement json, Type member, JsonDeserializationContext context) { int myType = json.getAsJsonObject().get("type").getAsInt(); switch (myType) { case 1: return context.deserialize(json, Silver.class); case 2: return context.deserialize(json, Gold.class); default: return null; } } } 

而且...输出

 Cloned! { "title": "MyClub", "members": [ { "silverData": "SomeSilverData", "clsname": "gson.test.Silver", "type": 1, "name": "Jack" }, { "goldData": "SomeGoldData", "extraData": "Extra Gold Data", "clsname": "gson.test.Gold", "type": 2, "name": "Jill" }, { "silverData": "SomeSilverData", "clsname": "gson.test.Silver", "type": 1, "name": "Mike" } ] } 

我应该注意到,我的真实用例是性能不应该成为问题,我正在从jSon文本文件加载对象缓存,因此执行此代码的频率使得性能远不如可维护性重要。