I'm trying to serialize/deserialize an object, that involves polymorphism, into JSON using Gson.

以下是我的序列化代码:

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

下面是我继承的类(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;
    }
}

我意识到我可以使用适配器来实现这一点,但问题是我正在序列化基类类型ObixBaseObj的集合.大约有25个类继承自此.我怎样才能优雅地完成这项工作?

推荐答案

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

现在这是我用来测试整个东西的代码:

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);

}

这是我的死刑:

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

也许您想要将序列化和反序列化统一到一个唯一的对象中,您应该判断TypeAdapter类,或者使用实现这两个接口的对象进行实验.

Json相关问答推荐

使用更高级别架构中的字段值在$def内实现约束

NiFi QueryRecord处理器- Select 可选的JSON属性

如何将具有相同 struct 的多个JSON文件中的对象数组合并成一个数组?

如何使用jolt规范将一个对象添加到另一个对象中并删除该对象

如何 Select 一个值,这是可选的 - 使用 jq

无法在 json --data 中使用变量

JOLT JSON 将值从一对多转换为一对一

为什么根据其他工具,来自 aws rds 的 JSON 在 Docker 中格式错误运行?

使用带有逗号的字段名称构建 struct

在 rust 中从 API 反序列化 serde_json

如何在生产环境中更改 Flutter 应用程序中的数据模型?

使用 jq 将键值行转换为 json

序列化为json时如何忽略空列表?

Android 上的 JSON - 序列化

谷歌浏览器不允许我放置断点

将 PHP 结果数组转换为 JSON

使用适用于 Python 的 Google API - 我从哪里获取 client_secrets.json 文件?

如何在dart Flutter 中将json字符串转换为json对象?

在 iPhone 上解析 JSON 日期

使用 JSON.NET 序列化/反序列化对象字典