Gson类型适配器与自定义Deseralizer [英] Gson Type Adapter vs. Custom 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 $ c的值创建哪个类$ c>将包含在目标对象中的属性。您的反序列化器需要对传入的 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屋!
查看全文