本文介绍了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.

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

07-29 13:21
查看更多