Gson类型适配器与自定义Deseralizer [英] Gson Type Adapter vs. Custom Deseralizer

查看:98
本文介绍了Gson类型适配器与自定义Deseralizer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



First Club Class

  package gson.test; 
import java.util.ArrayList;

import com.google.gson.Gson;

public class Club {
public static void main(String [] args){
//设置一个有2个成员的俱乐部
Club myClub = new Club() ;
myClub.addMember(new Silver());
myClub.addMember(new Gold());

//序列化为JSON
Gson gson = new Gson();
String myJsonClub = gson.toJson(myClub);
System.out.println(myJsonClub);

//反序列化到俱乐部
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 ;
}
返回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);}
}

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

 包gson。测试; 
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));


$ / code $ / pre

最后输出

  {title:MyClub,members:[{silverData:SomeSilverData,type:1,name: },{goldData:SomeGoldData,type:2,name:}]} 
线程main中的异常java.lang.RuntimeException:无法调用公共gson。 com.google.gson.internal.ConstructorConstructor $ 3.construct(ConstructorConstructor.java:107)
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $适配器()无参数
$ Adapter .read(ReflectiveTypeAdapterFactory.java:186)
...


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



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

看起来你会知道根据 type 将包含在目标对象中的属性。您的反序列化器需要对传入的 JsonElement 对象进行查找,读取


  • code> type 属性,确定类型
  • 调用 context.deserialize()类和传递给您的元素

  • 如果类型丢失或无效,则会抛出错误



你的类型适配器必须更复杂。类型适配器的输入是流,而不是元素/子树。您可以完全从流中加载下一个值,解析它,然后执行解串器完成的操作,这是没有意义的,您可以使用解串器接口。或者,您可以读取流,查看它们的属性,将它们保存到本地变量中,直到找到类型属性(您无法预测其位置),然后读完其余的属性,并根据类型创建最终的 Gold / Silver 对象,属性读取并保存。


The example below shows a class (Club) that contains a collection of an abstract class (Member). I'm confused as to whether I need a TypeAdapter or JsonDeserializer to make the Deserialization work correctly. 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. 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. Your deserializer will need to

  • look into the passed JsonElement object, read the type property, determine the type
  • call context.deserialize() with the class and the same element that was passed to you
  • 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.

这篇关于Gson类型适配器与自定义Deseralizer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆