简体   繁体   English

Gson Type Adapter与Custom Deseralizer

[英]Gson Type Adapter vs. Custom Deseralizer

The example below shows a class (Club) that contains a collection of an abstract class (Member). 下面的示例显示了一个包含抽象类(Member)集合的类(Club)。 I'm confused as to whether I need a TypeAdapter or JsonDeserializer to make the Deserialization work correctly. 我很困惑我是否需要TypeAdapter或JsonDeserializer来使反序列化正常工作。 Serialization works just fine without any help, but Deserialization is throwing exceptions. 序列化在没有任何帮助的情况下工作正常,但反序列化会抛出异常。 To illustrate I've built the following "clone" test. 为了说明我已经构建了以下“克隆”测试。 If anyone could show a working example I would be very grateful. 如果有人能展示一个有效的例子,我将非常感激。

First Club Class 第一俱乐部课程

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

Now the Abstract Base Class Member 现在是抽象基类成员

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

And two concrete sub-classes of Member (Gold and Silver) 会员的两个具体子类(金银)

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

And finally the output 最后输出

    {"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)
...

You can do both. 你可以做到这两点。 Which one you pick depends really on potential performance impact, and how much code are willing to write. 您选择哪一个取决于潜在的性能影响,以及愿意编写多少代码。

Deserializers are more expensive. 反序列化器更昂贵。 That is because the input to deserializer is a json tree, and GSon will have to create a full JsonElement subtree of the property that matches your class, before it can pass it to your deserializer. 这是因为反序列化器的输入是一个json树,并且GSon必须创建一个与您的类匹配的属性的完整JsonElement子树,然后才能将它传递给您的反序列化器。 If your classes have a lot of nesting, that cost increases. 如果你的课程有很多嵌套,那么这个成本会增加。 For plain objects, it will be negligible. 对于普通物体,它可以忽略不计。

It seems that you will know which class to create based on the value of type property that will be included in target object. 您似乎将根据将包含在目标对象中的type属性的值知道要创建的类。 Your deserializer will need to 你的解串器需​​要

  • look into the passed JsonElement object, read the type property, determine the type 查看传递的JsonElement对象,读取type属性,确定类型
  • call context.deserialize() with the class and the same element that was passed to you 使用类和传递给您的相同元素调用context.deserialize()
  • throw an error if type was missing or invalid 如果类型丢失或无效,则抛出错误

Your type adapter will have to be more complex. 您的类型适配器必须更复杂。 The input to the type adapter is stream, not an element/subtree. 类型适配器的输入是流,而不是元素/子树。 You can load the next value entirely from the stream, parse it, and then do exactly what deserializer did, which doesn't make sense and you can just use the deserializer interface. 您可以完全从流中加载下一个值,解析它,然后完全按照反序列化程序执行的操作,这没有意义,您只需使用反序列化器接口即可。 Alternatively, you can read the stream, see what properties there are, save them into local variables, until you get to the type property (you can't predict its location), then finish reading the remainder of the properties, and create your final Gold / Silver objects based on type, and all the properties read and saved. 或者,您可以阅读流,查看有哪些属性,将它们保存到局部变量中,直到找到type属性(无法预测其位置),然后读完剩余的属性,然后创建最终属性基于类型的Gold / Silver对象,以及读取和保存的所有属性。

Ok, real working example (I'm pretty sure this time). 好的,真正有效的例子(我很确定这一次)。

The Club 该俱乐部

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

The Member Abstract Class 会员抽象类

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

The Concrete Sub-Classes Silver and Gold 混凝土子类银和金

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

The Custom Member Serailizer 定制会员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;
        }
    }
}

The custom Deserializer 自定义反序列化器

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

And... the output 而......输出

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"
    }
  ]
}

I should note that my real use-case is one where performance should not be an issue, I'm loading a cache of objects from jSon text files so the frequency with this code is executed makes performance much less important than maintainability. 我应该注意到,我的实际用例是性能不应该是一个问题,我正在从jSon文本文件加载对象的缓存,因此执行此代码的频率使得性能远不如可维护性重要。

It looks like serializing/deserializing class hierarchies is a common problem. 看起来像序列化/反序列化类层次结构是一个常见问题。

There is even an "official" solution, inside extras directory of the official source repo (unfortunately it is not part of the Maven package though). 甚至还有一个“官方”解决方案,在官方源代码库的extras目录中(不幸的是它不是Maven包的一部分)。

Please check: 请检查:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM