Gson序列化多态对象列表 [英] Gson serialize a list of polymorphic objects

查看:133
本文介绍了Gson序列化多态对象列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用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;

$ b

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

公共总长;
公共长号码;

$ b

ExtendedClass2.java

  public class ExtendedClass2 extends BaseClass {

@Override
public String toString(){
returnExtendedClass2 [total =+ total +,list =+ list +,isA =
+ isA +,x =+ x +];
}

public ExtendedClass2(){
isA =ExtendedClass2;
}

公共总长;

$ b

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

返回列表;



$ b

CustomSerializer.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 and obixBatchout 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 the isA 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.

Pro You 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).

Cons You 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序列化多态对象列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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