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

Java Gson类型适配器与自定义反序列化程序,java,gson,Java,Gson,下面的示例显示了包含抽象类(成员)集合的类(俱乐部)。我不清楚是否需要TypeAdapter或JsonDeserializer来正确地进行反序列化。序列化可以在没有任何帮助的情况下正常工作,但反序列化会引发异常。为了说明这一点,我构建了以下“克隆”测试。如果有人能拿出一个有效的例子,我将不胜感激 头等舱 package gson.test; import java.util.ArrayList; import com.google.gson.Gson; public class Club {

下面的示例显示了包含抽象类(成员)集合的类(俱乐部)。我不清楚是否需要TypeAdapter或JsonDeserializer来正确地进行反序列化。序列化可以在没有任何帮助的情况下正常工作,但反序列化会引发异常。为了说明这一点,我构建了以下“克隆”测试。如果有人能拿出一个有效的例子,我将不胜感激

头等舱

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<Member> members = new ArrayList<Member>();

    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); }
}
以及构件的两个具体子类(黄金和白银)

最后是输出

    {"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)
...
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树,GSon必须创建与您的类匹配的属性的完整JsonElement子树,然后才能将其传递给反序列化程序。如果您的类有很多嵌套,那么成本会增加。对于普通对象,它可以忽略不计

似乎您将知道基于将包含在目标对象中的
type
属性的值创建哪个类。您的反序列化程序需要

  • 查看传递的
    JsonElement
    对象,读取
    type
    属性,确定类型
  • 使用类和传递给您的相同元素调用
    context.deserialize()
  • 如果类型丢失或无效,则引发错误
您的类型适配器必须更加复杂。类型适配器的输入是流,而不是元素/子树。您可以完全从流中加载下一个值,对其进行解析,然后执行反序列化程序所做的操作,这没有意义,您可以只使用反序列化程序接口。或者,您可以读取流,查看有哪些属性,将它们保存到局部变量中,直到到达
类型
属性(您无法预测其位置),然后完成其余属性的读取,并根据类型创建最终的
黄金
/
白银
对象,读取并保存所有属性。

好的,真正的工作示例(这次我非常确定)

俱乐部

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<Member> members = new ArrayList<Member>();

    public boolean equals(Object club) {
        Club that = (Club) club;
        if (!this.title.equals(that.title)) return false;
        for (int i=0; i<this.members.size(); i++) {
            Member member1 = this.getMember(i);
            Member member2 = that.getMember(i);
            if (! member1.equals(member2)) 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 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<Member> {

    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;
        }
    }
}

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

序列化/反序列化类层次结构似乎是一个常见问题

在官方源repo的
extras
目录中甚至有一个“官方”解决方案(不幸的是,它不是Maven包的一部分)

请检查:

  • 解释如下:
  • 解决方案:。建议只复制/粘贴源文件

equals()方法必须采用
Object
参数类型,而不是
Club
谢谢,我会花一点时间来理解。明白了,谢谢,我下次会投票。我匆忙通过测试,我的序列化没有像我想象的那样工作,因此,看起来我还需要一个特殊的序列化程序来深入研究集合的子类型。当我有一个工作示例时,我将继续测试并发布最后一个工作示例。
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)); 
    }
}
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<Member> {

    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<Member> {
    @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"
    }
  ]
}