当前位置: 首页 > 知识库问答 >
问题:

Gson序列化多态对象列表

赵俊晤
2023-03-14

我正在尝试使用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: BatchInobixBatchout字符串丢失)。这是我的基类:

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个类继承自此。我怎样才能优雅地完成这项工作?

共有3个答案

斜俊
2023-03-14

我感谢这里的其他答案,这些答案引导我走上了解决这个问题的道路。我将RuntimeTypeAdapterFactory与反射结合使用。

我还创建了一个助手类,以确保使用了正确配置的Gson。

在GsonHelper类中的一个静态块中,我有以下代码遍历我的项目,以查找并注册所有适当的类型。我所有将通过JSON转换的对象都是Jsonable的一个子类型。您需要更改以下内容:

>

  • 反射中的my.project应该是您的包名称。
  • Jsonable.class是我的基类。代替你的。
  • 我喜欢让字段显示完整的规范名称,但是很明显,如果你不想/不需要它,你可以省略呼叫的那一部分来注册子类型。同样的道理也适用于RuntimeAdapterFactory中的class Name;我已经使用了type字段的数据项。

    private static final GsonBuilder gsonBuilder = new GsonBuilder()
        .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
        .excludeFieldsWithoutExposeAnnotation()
        .setPrettyPrinting();
    
    static {
    Reflections reflections = new Reflections("my.project");
    
    Set<Class<? extends Jsonable>> allTypes = reflections.getSubTypesOf(Jsonable.class);
    for (Class< ? extends Jsonable> serClass : allTypes){
        Set<?> subTypes = reflections.getSubTypesOf(serClass);
        if (subTypes.size() > 0){
            RuntimeTypeAdapterFactory<?> adapterFactory = RuntimeTypeAdapterFactory.of(serClass, "className");
            for (Object o : subTypes ){
                Class c = (Class)o;
                adapterFactory.registerSubtype(c, c.getCanonicalName());
            }
            gsonBuilder.registerTypeAdapterFactory(adapterFactory);
        }
    }
    }
    
    public static Gson getGson() {
        return gsonBuilder.create();
    }
    

  • 诸葛康胜
    2023-03-14

    我认为定制的序列化程序/反序列化程序是唯一可以继续进行的方法,我试图向您推荐我发现的最简洁的实现方法。我很抱歉没有使用你的类,但想法是一样的(我只想要至少一个基类和两个扩展类)。

    基本舱。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;
       
     }
    

    扩展到1级。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;
        
    }
    

    扩展类2。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;
        
    }
    

    自定义反序列化程序。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;
    
        }
    
    }
    

    自定义序列化程序。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]
    

    一些解释:这个技巧是由序列化器/反序列化器中的另一个Gson完成的。我使用isA字段来确定正确的类。为了更快,我使用映射将isA字符串与相应的类相关联。然后,我使用第二个Gson对象进行适当的序列化/反序列化。我将其声明为静态,这样就不会因为多次分配Gson而减慢序列化/反序列化的速度。

    实际上,你不需要编写比这更多的代码,所有的工作都由Gson来完成。您只需记住在映射中放入一个新的子类(异常提醒您这一点)。

    你有两张地图。我认为我的实现可以进行一些改进,以避免地图重复,但我把它们留给了您(或者未来的编辑器,如果有的话)。

    也许你想将序列化和反序列化统一到一个唯一的对象中,你应该检查TypeAdapter类或者尝试一个实现这两个接口的对象。

    宗鸿博
    2023-03-14

    有一个简单的解决方案:Gson的RuntimeTypeAdapterFactory(来自com.google.code.Gson:Gson extras:$gsonVersion)。您不必编写任何序列化程序,这个类可以为您完成所有工作。用你的代码试试这个:

        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);
    
        RuntimeTypeAdapterFactory<ObixBaseObj> adapter = 
                        RuntimeTypeAdapterFactory
                       .of(ObixBaseObj.class)
                       .registerSubtype(ObixBaseObj.class)
                       .registerSubtype(ObixOp.class);
    
    
        Gson gson2=new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create();
        Gson gson = new Gson();
        System.out.println(gson.toJson(lobbyObj));
        System.out.println("---------------------");
        System.out.println(gson2.toJson(lobbyObj));
    
    }
    

    输出:

    {"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch","children":[]}]}
    ---------------------
    {
      "type": "ObixBaseObj",
      "obix": "obj",
      "is": "obix:Lobby",
      "children": [
        {
          "type": "ObixOp",
          "in": "obix:BatchIn",
          "out": "obix:BatchOut",
          "obix": "op",
          "name": "batch",
          "children": []
        }
      ]
    }
    

    编辑:更好的工作示例。

    您说过大约有25个类继承自ObixBaseObj

    我们开始写一个新的类,GsonUtils

    public class GsonUtils {
    
        private static final GsonBuilder gsonBuilder = new GsonBuilder()
                .setPrettyPrinting();
    
        public static void registerType(
                RuntimeTypeAdapterFactory<?> adapter) {
            gsonBuilder.registerTypeAdapterFactory(adapter);
        }
    
        public static Gson getGson() {
            return gsonBuilder.create();
        }
    

    每次我们需要一个Gson对象时,我们都会调用new Gson()

    GsonUtils.getGson()
    

    我们将以下代码添加到ObixBaseObj中:

    public class ObixBaseObj {
        protected String obix;
        private String display;
        private String displayName;
        private String name;
        private String is;
        private ArrayList<ObixBaseObj> children = new ArrayList<ObixBaseObj>();
        // new code
        private static final RuntimeTypeAdapterFactory<ObixBaseObj> adapter = 
                RuntimeTypeAdapterFactory.of(ObixBaseObj.class);
    
        private static final HashSet<Class<?>> registeredClasses= new HashSet<Class<?>>();
    
        static {
            GsonUtils.registerType(adapter);
        }
    
        private synchronized void registerClass() {
            if (!registeredClasses.contains(this.getClass())) {
                registeredClasses.add(this.getClass());
                adapter.registerSubtype(this.getClass());
            }
        }
        public ObixBaseObj() {
            registerClass();
            obix = "obj";
        }
    

    为什么啊?因为每次实例化这个类或ObixBaseObj的子类时,它将在RuntimeTypeAdapter中注册的类

    在儿童课程中,只需要一点小小的改变:

    public class ObixOp extends ObixBaseObj {
        private String in;
        private String out;
    
        public ObixOp() {
            super();
            obix = "op";
        }
    
        public ObixOp(String in, String out) {
            super();
            obix = "op";
            this.in = in;
            this.out = out;
        }
    

    工作示例:

    public static void main(String[] args) {
    
            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 = GsonUtils.getGson();
            System.out.println(gson.toJson(lobbyObj));
    
        }
    

    输出:

    {
      "type": "ObixBaseObj",
      "obix": "obj",
      "is": "obix:Lobby",
      "children": [
        {
          "type": "ObixOp",
          "in": "obix:BatchIn",
          "out": "obix:BatchOut",
          "obix": "op",
          "name": "batch",
          "children": []
        }
      ]
    }
    

    我希望有帮助。

     类似资料:
    • 我正在尝试使用Gson将一个涉及多态性的对象序列化/反序列化为JSON。 这是我的序列化代码: 结果是这样的: 序列化主要工作,除了它缺少继承成员的内容(特别是和字符串丢失)。这是我的基类: 下面是我继承的类(ObixOp)的样子: 我意识到我可以使用适配器来实现这一点,但问题是我正在序列化一个基类类型的集合。大约有25个类继承自此。我怎样才能优雅地完成这项工作?

    • 问题内容: 使用Gson 2.2.2,我正在尝试序列化POJO(行为)的数组列表。 我有一个适配器,几乎是我在网上看到的一个副本: 我这样注册: 然后,当我尝试序列化我的ArrayList时: 我出现堆栈溢出。 似乎在线上: 它开始递归循环,一次又一次地通过我的序列化器。那么,我该如何注册它以免发生这种情况?我需要序列化列表并保持多态性。 问题答案: 看起来您发现了JsonSerializer文档

    • 主要内容:示例我们将一个Java对象序列化为一个Json文件,然后读取该Json文件以获取对象。 在这个例子中,创建一个类。 然后将对象列化后存储在文件中,该文件将具有对象的json表示形式。 示例 在中创建一个名为的Java类文件,参考以下代码 - 执行上面示例代码,得到以下结果 -

    • 我有一个接口,其中和实现。服务器会返回这样的响应- 相应的类来建模它是 中的声明它是哪种车辆。我需要反序列化的成一个对象取决于。如果是,我需要在反序列化和期间使用反序列化。 如何使用gson实现这一点? 编辑-这篇文章的不同之处在于类类型()包含在需要反序列化的中。这里不是。如果在内,我可以为编写自定义反序列化程序,检查,并进行相应的反序列化。但是我想我需要为编写一个自定义反序列化程序,在这里我创

    • 我想将这个对象序列化为JSON字符串 并得到如下结果: 我试着用 但不是这样,我得到了: 个性化序列化: 欢迎提出任何建议 谢谢,马里奥

    • 这里有人知道如何使用TypeAdapterFactory基于“type”字段进行反序列化而不需要将整个json流读取到JsonElement对象树中吗?