问题描述
我试图使用Gson将包含多态性的对象序列化/反序列化为JSON。
这是我的序列化代码:
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs(obix:Lobby);
ObixOp batchOp = new ObixOp();
batchOp.setName(batch);
batchOp.setIn(obix:BatchIn);
batchOp.setOut(obix:BatchOut);
lobbyObj.addChild(batchOp);
Gson gson = new Gson();
System.out.println(gson.toJson(lobbyObj));
结果如下:
{obix:obj,is:obix:Lobby,children:[{obix:op,name:batch}]}
序列化主要起作用,除了缺少继承成员的内容(特别是 obix:BatchIn
和 obixBatchout
字符串缺失)。
这里是我的基类:
public class ObixBaseObj {
protected String obix;
私人字符串显示;
private String displayName;
private ArrayList< ObixBaseObj>儿童;
public ObixBaseObj()
{
obix =obj;
}
public void setName(String name){
this.name = name;
}
...
}
这是我的继承类(ObixOp)如下所示:
public class ObixOp extends ObixBaseObj {
private String in;
private String out;
public ObixOp(){
obix =op;
}
public ObixOp(String in,String out){
obix =op;
this.in = in;
this.out = out;
}
public String getIn(){
return in;
}
public void setIn(String in){
this.in = in;
}
public String getOut(){
return out;
}
public void setOut(String out){
this.out = out;
$ b我知道我可以使用适配器来做这件事,但问题是我正在序列化一个基类类型集合ObixBaseObj
。大约有25个类从这个继承。解决方案我认为自定义序列化器/反序列化器是唯一可行的方法,我尝试过为您提出实现它的最紧凑的方式,我发现了。我很抱歉不使用你的类,但想法是一样的(我只想要至少一个基类和两个扩展类)。
BaseClass.java
public class BaseClass {
@Override
public String toString( ){
returnBaseClass [list =+ list +,isA =+ isA +,x =+ x +];
}
public ArrayList< BaseClass> list = new ArrayList< BaseClass>();
保护字符串isA =BaseClass;
public int x;
$ bExtendedClass1.java
public class ExtendedClass1 extends BaseClass {
@Override
public String toString(){
returnExtendedClass1 [total =+ total +,number =+ number
+,list =+ list +,isA =+ isA +,x =+ x +] ;
}
public ExtendedClass1(){
isA =ExtendedClass1;
}
公共总长;
公共长号码;
$ bExtendedClass2.java
public class ExtendedClass2 extends BaseClass {
@Override
public String toString(){
returnExtendedClass2 [total =+ total +,list =+ list +,isA =
+ isA +,x =+ x +];
}
public ExtendedClass2(){
isA =ExtendedClass2;
}
公共总长;
$ bCustomDeserializer.java
public class CustomDeserializer实现了JsonDeserializer< List< BaseClass>> {
私人静态地图< String,Class> map = new TreeMap< String,Class>();
static {
map.put(BaseClass,BaseClass.class);
map.put(ExtendedClass1,ExtendedClass1.class);
map.put(ExtendedClass2,ExtendedClass2.class);
}
public List< BaseClass> deserialize(JsonElement json,Type typeOfT,
JsonDeserializationContext context)throws JsonParseException {
List list = new ArrayList< BaseClass>();
JsonArray ja = json.getAsJsonArray(); (JsonElement je:ja){
字符串类型= je.getAsJsonObject()。get(isA)。getAsString();
Class c = map.get(type);
if(c == null)
throw new RuntimeException(Unknow class:+ type);
list.add(context.deserialize(je,c));
}
返回列表;
$ bCustomSerializer.java
public class CustomSerializer实现JsonSerializer< ArrayList< BaseClass>> {
私人静态地图< String,Class> map = new TreeMap< String,Class>();
static {
map.put(BaseClass,BaseClass.class);
map.put(ExtendedClass1,ExtendedClass1.class);
map.put(ExtendedClass2,ExtendedClass2.class);
$ b @Override
public JsonElement serialize(ArrayList< BaseClass> src,Type typeOfSrc,
JsonSerializationContext context){
if(src == null )
返回null;
else {
JsonArray ja = new JsonArray();
for(BaseClass bc:src){
Class c = map.get(bc.isA);
if(c == null)
throw new RuntimeException(Unknow class:+ bc.isA);
ja.add(context.serialize(bc,c));
}
return ja;
}
}
}
现在这是代码我执行以测试整个事情:
public static void main(String [] args){
BaseClass c1 = new BaseClass();
ExtendedClass1 e1 = new ExtendedClass1();
e1.total = 100L;
e1.number = 5L;
ExtendedClass2 e2 = new ExtendedClass2();
e2.total = 200L;
e2.x = 5;
BaseClass c2 = new BaseClass();
c1.list.add(e1);
c1.list.add(e2);
c1.list.add(c2);
List< BaseClass> al = new ArrayList< BaseClass>();
//这是序列化之前BaseClass的实例
System.out.println(c1);
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(al.getClass(),new CustomDeserializer());
gb.registerTypeAdapter(al.getClass(),new CustomSerializer());
Gson gson = gb.create();
字符串json = gson.toJson(c1);
//这是相应的json
System.out.println(json);
BaseClass newC1 = gson.fromJson(json,BaseClass.class);
System.out.println(newC1);
$ / code $ / pre
这是我的执行:
BaseClass [list = [ExtendedClass1 [total = 100,number = 5,list = [],isA = ExtendedClass1,x = 0],ExtendedClass2 [total = 200,list = [],isA = ExtendedClass2,x = 5],BaseClass [list = [],isA = BaseClass,x = 0]],isA = BaseClass,x = 0]
{list [{ 总:100, 数量:5 列表:[], ISA: ExtendedClass1, ×:0},{ 总:200, 列表:[], ISA: ExtendedClass2, ×:5},{ 列表:[], ISA: BaseClass的, ×:0}], ISA: BaseClass的, x 的:0}
BaseClass [list = [ExtendedClass1 [total = 100,number = 5,list = [],isA = ExtendedClass1,x = 0],ExtendedClass2 [total = 200,list = [],isA = ExtendedClass2 ,x = 5],BaseClass [list = [],isA = BaseClass,x = 0]],isA = BaseClass,x = 0]
一些解释:诀窍由串行器/解串器内的另一个Gson完成。我只用
isA
字段来找出正确的类。为了加快速度,我使用映射将isA
字符串与相应的类相关联。然后,我使用第二个Gson对象进行正确的序列化/反序列化。我声明它是静态的,所以你不会因为多次分配Gson而减慢序列化/反序列化。
Pro
您实际上不会编写比此代码更多的代码,您可以让Gson完成所有工作。您只需要记住将新的子类放入地图中(该例外会让您想起这一点)。
缺点
You你有两张地图。我认为我的实现可以稍微改进以避免地图重复,但是我将它们留给了您(或者对未来的编辑,如果有的话)。
也许你想将序列化和反序列化证明为一个唯一的对象,你应该检查
TypeAdapter
类或实验用一个实现这两个接口的对象。I'm trying to serialize/deserialize an object, that involves polymorphism, into JSON using Gson.
This is my code for serializing:
ObixBaseObj lobbyObj = new ObixBaseObj(); lobbyObj.setIs("obix:Lobby"); ObixOp batchOp = new ObixOp(); batchOp.setName("batch"); batchOp.setIn("obix:BatchIn"); batchOp.setOut("obix:BatchOut"); lobbyObj.addChild(batchOp); Gson gson = new Gson(); System.out.println(gson.toJson(lobbyObj));
Here's the result:
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch"}]}
The serialization mostly works, except its missing the contents of inherited members (In particular
obix:BatchIn
andobixBatchout
strings are missing).Here's my base class:public class ObixBaseObj { protected String obix; private String display; private String displayName; private ArrayList<ObixBaseObj> children; public ObixBaseObj() { obix = "obj"; } public void setName(String name) { this.name = name; } ... }
Here's what my inherited class (ObixOp) looks like:
public class ObixOp extends ObixBaseObj { private String in; private String out; public ObixOp() { obix = "op"; } public ObixOp(String in, String out) { obix = "op"; this.in = in; this.out = out; } public String getIn() { return in; } public void setIn(String in) { this.in = in; } public String getOut() { return out; } public void setOut(String out) { this.out = out; } }
I realize I could use an adapter for this, but the problem is that I'm serializing a collection of base class type
ObixBaseObj
. There are about 25 classes that inherits from this. How can I make this work elegantly?解决方案I think that a custom serializer/deserializer is the only way to proceed and I tried to propose you the most compact way to realize it I have found. I apologize for not using your classes, but the idea is the same (I just wanted at least 1 base class and 2 extended classes).
BaseClass.java
public class BaseClass{ @Override public String toString() { return "BaseClass [list=" + list + ", isA=" + isA + ", x=" + x + "]"; } public ArrayList<BaseClass> list = new ArrayList<BaseClass>(); protected String isA="BaseClass"; public int x; }
ExtendedClass1.java
public class ExtendedClass1 extends BaseClass{ @Override public String toString() { return "ExtendedClass1 [total=" + total + ", number=" + number + ", list=" + list + ", isA=" + isA + ", x=" + x + "]"; } public ExtendedClass1(){ isA = "ExtendedClass1"; } public Long total; public Long number; }
ExtendedClass2.java
public class ExtendedClass2 extends BaseClass{ @Override public String toString() { return "ExtendedClass2 [total=" + total + ", list=" + list + ", isA=" + isA + ", x=" + x + "]"; } public ExtendedClass2(){ isA = "ExtendedClass2"; } public Long total; }
CustomDeserializer.java
public class CustomDeserializer implements JsonDeserializer<List<BaseClass>> { private static Map<String, Class> map = new TreeMap<String, Class>(); static { map.put("BaseClass", BaseClass.class); map.put("ExtendedClass1", ExtendedClass1.class); map.put("ExtendedClass2", ExtendedClass2.class); } public List<BaseClass> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { List list = new ArrayList<BaseClass>(); JsonArray ja = json.getAsJsonArray(); for (JsonElement je : ja) { String type = je.getAsJsonObject().get("isA").getAsString(); Class c = map.get(type); if (c == null) throw new RuntimeException("Unknow class: " + type); list.add(context.deserialize(je, c)); } return list; } }
CustomSerializer.java
public class CustomSerializer implements JsonSerializer<ArrayList<BaseClass>> { private static Map<String, Class> map = new TreeMap<String, Class>(); static { map.put("BaseClass", BaseClass.class); map.put("ExtendedClass1", ExtendedClass1.class); map.put("ExtendedClass2", ExtendedClass2.class); } @Override public JsonElement serialize(ArrayList<BaseClass> src, Type typeOfSrc, JsonSerializationContext context) { if (src == null) return null; else { JsonArray ja = new JsonArray(); for (BaseClass bc : src) { Class c = map.get(bc.isA); if (c == null) throw new RuntimeException("Unknow class: " + bc.isA); ja.add(context.serialize(bc, c)); } return ja; } } }
and now this is the code I executed to test the whole thing:
public static void main(String[] args) { BaseClass c1 = new BaseClass(); ExtendedClass1 e1 = new ExtendedClass1(); e1.total = 100L; e1.number = 5L; ExtendedClass2 e2 = new ExtendedClass2(); e2.total = 200L; e2.x = 5; BaseClass c2 = new BaseClass(); c1.list.add(e1); c1.list.add(e2); c1.list.add(c2); List<BaseClass> al = new ArrayList<BaseClass>(); // this is the instance of BaseClass before serialization System.out.println(c1); GsonBuilder gb = new GsonBuilder(); gb.registerTypeAdapter(al.getClass(), new CustomDeserializer()); gb.registerTypeAdapter(al.getClass(), new CustomSerializer()); Gson gson = gb.create(); String json = gson.toJson(c1); // this is the corresponding json System.out.println(json); BaseClass newC1 = gson.fromJson(json, BaseClass.class); System.out.println(newC1); }
This is my execution:
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0] {"list":[{"total":100,"number":5,"list":[],"isA":"ExtendedClass1","x":0},{"total":200,"list":[],"isA":"ExtendedClass2","x":5},{"list":[],"isA":"BaseClass","x":0}],"isA":"BaseClass","x":0} BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
Some explanations: the trick is done by another Gson inside the serializer/deserializer. I use just
isA
field to spot the right class. To go faster, I use a map to associate theisA
string to the corresponding class. Then, I do the proper serialization/deserialization using the second Gson object. I declared it as static so you won't slow serialization/deserialization with multiple allocation of Gson.ProYou actually do not write code more code than this, you let Gson do all the work. You have just to remember to put a new subclass into the maps (the exception reminds you of that).
ConsYou you have two maps. I think that my implementation can refined a bit to avoid map duplications, but I left them to you (or to future editor, if any).
Maybe you want to unificate serialization and deserialization into a unique object, you should be check the
TypeAdapter
class or experiment with an object that implements both interfaces.这篇关于Gson序列化多态对象列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!