Gson类型适配器与自定义反序列化器

au9on6nz  于 2022-11-06  发布在  其他
关注(0)|答案(3)|浏览(168)

下面的例子显示了一个类(Club),它包含一个抽象类(Member)的集合。我不知道我是否需要TypeAdapter或JsonDeserializer来使反序列化正确工作。序列化在没有任何帮助的情况下工作得很好,但是反序列化会抛出异常。为了说明我构建了下面的“克隆”测试。如果有人能提供一个工作示例,我将非常感激。
头等舱

  1. package gson.test;
  2. import java.util.ArrayList;
  3. import com.google.gson.Gson;
  4. public class Club {
  5. public static void main(String[] args) {
  6. // Setup a Club with 2 members
  7. Club myClub = new Club();
  8. myClub.addMember(new Silver());
  9. myClub.addMember(new Gold());
  10. // Serialize to JSON
  11. Gson gson = new Gson();
  12. String myJsonClub = gson.toJson(myClub);
  13. System.out.println(myJsonClub);
  14. // De-Serialize to Club
  15. Club myNewClub = gson.fromJson(myJsonClub, Club.class);
  16. System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed");
  17. }
  18. private String title = "MyClub";
  19. private ArrayList<Member> members = new ArrayList<Member>();
  20. public boolean equals(Club that) {
  21. if (!this.title.equals(that.title)) return false;
  22. for (int i=0; i<this.members.size(); i++) {
  23. if (! this.getMember(i).equals(that.getMember(i))) return false;
  24. }
  25. return true;
  26. }
  27. public void addMember(Member newMember) { members.add(newMember); }
  28. public Member getMember(int i) { return members.get(i); }
  29. }

现在是抽象基类成员

  1. package gson.test;
  2. public abstract class Member {
  3. private int type;
  4. private String name = "";
  5. public int getType() { return type; }
  6. public void setType(int type) { this.type = type; }
  7. public boolean equals(Member that) {return this.name.equals(that.name);}
  8. }

以及会员的两个具体子类(黄金级和银级)

  1. package gson.test;
  2. public class Gold extends Member {
  3. private String goldData = "SomeGoldData";
  4. public Gold() {
  5. super();
  6. this.setType(2);
  7. }
  8. public boolean equals(Gold that) {
  9. return (super.equals(that) && this.goldData.equals(that.goldData));
  10. }
  11. }
  12. package gson.test;
  13. public class Silver extends Member {
  14. private String silverData = "SomeSilverData";
  15. public Silver() {
  16. super();
  17. this.setType(1);
  18. }
  19. public boolean equals(Silver that) {
  20. return (super.equals(that) && this.silverData.equals(that.silverData));
  21. }
  22. }

最后是输出

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

azpvetkf1#

你可以两者都做。你选择哪一个实际上取决于潜在的性能影响,以及有多少代码愿意写。
反序列化器的开销更大。这是因为反序列化器的输入是一个json树,GSon必须为匹配你的类的元素创建一个完整的JsonElement子树,然后才能将它传递给反序列化器。如果你的模型有很多嵌套,开销会增加。对于普通对象,开销可以忽略不计。
看起来您将知道基于将包含在目标对象中的type属性的值创建哪个类。

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

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

krugob8w

krugob8w2#

好的,这是一个真实的的工作示例(这次我非常确定)。
俱乐部

  1. package gson.test;
  2. import java.util.ArrayList;
  3. import com.google.gson.Gson;
  4. import com.google.gson.GsonBuilder;
  5. public class Club {
  6. public static void main(String[] args) {
  7. // Setup a Club with 2 members
  8. Club myClub = new Club();
  9. myClub.addMember(new Silver("Jack"));
  10. myClub.addMember(new Gold("Jill"));
  11. myClub.addMember(new Silver("Mike"));
  12. // Get the GSON Object and register Type Adapter
  13. GsonBuilder builder = new GsonBuilder();
  14. builder.registerTypeAdapter(Member.class, new MemberDeserializer());
  15. builder.registerTypeAdapter(Member.class, new MemberSerializer());
  16. builder.setPrettyPrinting();
  17. Gson gson = builder.create();
  18. // Serialize Club to JSON
  19. String myJsonClub = gson.toJson(myClub);
  20. // De-Serialize to Club
  21. Club myNewClub = gson.fromJson(myJsonClub, Club.class);
  22. System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed");
  23. System.out.println(gson.toJson(myNewClub));
  24. }
  25. private String title = "MyClub";
  26. private ArrayList<Member> members = new ArrayList<Member>();
  27. public boolean equals(Object club) {
  28. Club that = (Club) club;
  29. if (!this.title.equals(that.title)) return false;
  30. for (int i=0; i<this.members.size(); i++) {
  31. Member member1 = this.getMember(i);
  32. Member member2 = that.getMember(i);
  33. if (! member1.equals(member2)) return false;
  34. }
  35. return true;
  36. }
  37. public void addMember(Member newMember) { members.add(newMember); }
  38. public Member getMember(int i) { return members.get(i); }
  39. }

成员抽象类

  1. package gson.test;
  2. public abstract class Member {
  3. private String clsname = this.getClass().getName() ;
  4. private int type;
  5. private String name = "unknown";
  6. public Member() { }
  7. public Member(String theName) {this.name = theName;}
  8. public int getType() { return type; }
  9. public void setType(int type) { this.type = type; }
  10. public boolean equals(Object member) {
  11. Member that = (Member) member;
  12. return this.name.equals(that.name);
  13. }
  14. }

具体子类银和金

  1. package gson.test;
  2. public class Silver extends Member {
  3. private String silverData = "SomeSilverData";
  4. public Silver() {
  5. super();
  6. this.setType(1);
  7. }
  8. public Silver(String theName) {
  9. super(theName);
  10. this.setType(1);
  11. }
  12. public boolean equals(Object that) {
  13. Silver silver = (Silver)that;
  14. return (super.equals(that) && this.silverData.equals(silver.silverData));
  15. }
  16. }
  17. package gson.test;
  18. public class Gold extends Member {
  19. private String goldData = "SomeGoldData";
  20. private String extraData = "Extra Gold Data";
  21. public Gold() {
  22. super();
  23. this.setType(2);
  24. }
  25. public Gold(String theName) {
  26. super(theName);
  27. this.setType(2);
  28. }
  29. public boolean equals(Gold that) {
  30. Gold gold = (Gold) that;
  31. return (super.equals(that) && this.goldData.equals(gold.goldData));
  32. }
  33. }

自定义成员序列化程序

  1. package gson.test;
  2. import java.lang.reflect.Type;
  3. import com.google.gson.JsonElement;
  4. import com.google.gson.JsonSerializationContext;
  5. import com.google.gson.JsonSerializer;
  6. public class MemberSerializer implements JsonSerializer<Member> {
  7. public JsonElement serialize(Member src, Type member, JsonSerializationContext context) {
  8. switch (src.getType()) {
  9. case 1: return context.serialize((Silver)src);
  10. case 2: return context.serialize((Gold)src);
  11. default: return null;
  12. }
  13. }
  14. }

自定义反序列化程序

  1. package gson.test;
  2. import java.lang.reflect.Type;
  3. import com.google.gson.JsonDeserializationContext;
  4. import com.google.gson.JsonDeserializer;
  5. import com.google.gson.JsonElement;
  6. public class MemberDeserializer implements JsonDeserializer<Member> {
  7. @Override
  8. public Member deserialize(JsonElement json, Type member, JsonDeserializationContext context) {
  9. int myType = json.getAsJsonObject().get("type").getAsInt();
  10. switch (myType) {
  11. case 1: return context.deserialize(json, Silver.class);
  12. case 2: return context.deserialize(json, Gold.class);
  13. default: return null;
  14. }
  15. }
  16. }

还有输出

  1. Cloned!
  2. {
  3. "title": "MyClub",
  4. "members": [
  5. {
  6. "silverData": "SomeSilverData",
  7. "clsname": "gson.test.Silver",
  8. "type": 1,
  9. "name": "Jack"
  10. },
  11. {
  12. "goldData": "SomeGoldData",
  13. "extraData": "Extra Gold Data",
  14. "clsname": "gson.test.Gold",
  15. "type": 2,
  16. "name": "Jill"
  17. },
  18. {
  19. "silverData": "SomeSilverData",
  20. "clsname": "gson.test.Silver",
  21. "type": 1,
  22. "name": "Mike"
  23. }
  24. ]
  25. }

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

展开查看全部
j5fpnvbx

j5fpnvbx3#

看起来序列化/反序列化类层次结构是一个常见的问题。
甚至还有一个“官方”解决方案,在官方源代码存储库的extras目录中(不幸的是,它不是Maven包的一部分)。
请检查:

相关问题