Проблема десериализации Gson — если параметр типа является дочерним классом, не удалось десериализовать

#java #generics #gson #json-deserialization

#java #общие #gson #json-десериализация

Вопрос:

В мастер-классе для переменной Generic<Parent> generic я передаю дочерний объект main() . Во время сериализации я получаю правильный вывод. Но при десериализации дочерний объект отсутствует. Кто-нибудь может дать рекомендации.

 public class GenericSample {
    public static void main(String[] args) {
        Generic<Parent> generic = new Generic<Parent>();
        Child child = new Child();
        child.setName("I am child");
        generic.setT(child);

        Gson gson = new Gson();

        Master master = new Master();
        master.setId(2);
        master.setGeneric(generic);

        String valMaster = gson.toJson(master);
        System.out.println(valMaster);
        /*
         * Output: {"id":2,"generic":{"t":{"name":"I am child"}}}
         */

        Master master2 = gson.fromJson(valMaster, Master.class);
        String valMaster2 = gson.toJson(master2);
        System.out.println(valMaster2);
        /*
         * Child Object is missing
         * Output: {"id":2,"generic":{"t":{}}}
         */
    }

    static class Master {
        private int id;
        private Generic<Parent> generic;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Generic<Parent> getGeneric() {
            return generic;
        }

        public void setGeneric(Generic<Parent> generic) {
            this.generic = generic;
        }

    }

    static class Generic<T> {
        T t;

        public T getT() {
            return t;
        }

        public void setT(T t) {
            this.t = t;
        }
        
    }
    static class Parent {
        private String type;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

    }

    static class Child extends Parent {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

    }

}
  

Ответ №1:

Проблема

Gson пытается десериализовать общее значение в Parent , не Child . Поскольку существует type as null , вы не видите данных в десериализованном объекте, который отображается как {} . Если вы добавите child.setType("type"); , то выходные данные станут:

  • valMaster1 : {"id":2,"generic":{"t":{"name":"I am child","type":"type"}}}
  • valMaster2 : {"id":2,"generic":{"t":{"type":"type"}}}

Однако поле name отсутствует в Parent классе, но Child класс и Gson просто понятия не имеют, к какому подклассу Parent оно относится (если да), и полностью игнорируют значение, что является правильным поведением.

Решение

Я нахожу в основном два варианта (я использую конструктор all-args для краткости):

  1. Повысьте значение параметра универсального типа с верхней границей до Master класса и укажите конкретный Child тип в точке десериализации, используя com.google.gson.reflect.TypeToken и java.lang.reflect.Type :

     static class Master<T extends Parent> {
        private int id;
        private Generic<T> generic;
    
        /* getters, setters and all-args constructor */
    }
      
     Child child = new Child("I am child");
    Generic<Parent> generic = new Generic<>(child);
    Master<Parent> master = new Master<>(2, generic);
    
    Gson gson = new Gson();
    String valMaster = gson.toJson(master);
    System.out.println(valMaster);                           
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
    Type type = new TypeToken<Master<Child>>() {}.getType();
    Master<Child> master2 = gson.fromJson(valMaster, type);
    String valMaster2 = gson.toJson(master2);
    System.out.println(valMaster2);
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
      
  2. Жестко закодируйте конкретный универсальный тип Generic<Child> внутри Master класса. Десериализация упрощается, но дизайн менее гибкий:

     static class Master {
        private int id;
        private Generic<Child> generic;
    
        /* getters, setters and all-args constructor */
    }
      
     Child child = new Child("I am child");
    Generic<Child> generic = new Generic<>(child);
    Master master = new Master(2, generic);
    
    Gson gson = new Gson();
    String valMaster = gson.toJson(master);
    System.out.println(valMaster);
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
    Master master2 = gson.fromJson(valMaster, Master.class);
    String valMaster2 = gson.toJson(master2);
    System.out.println(valMaster2);
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
      

Комментарии:

1. Я действительно считаю, что OP запрашивает идеальную сериализацию / десериализацию в оба конца, а не жесткое кодирование дочерних классов ad-hoc.

2. @fluffy: Вы прочитали первую пару фрагментов? Это ничего не говорит о жестком кодировании .

3. Да, я это сделал. Type type = new TypeToken<Master<Child>>() {}.getType(); В вашем решении № 1.

4. Пожалуйста, найдите обходное решение по адресу pastebin.com/fXL4r1s9

5. Выглядит хорошо для меня. Однако вам необходимо удалить поле, type объявленное из Parent . Спасибо. Я узнал кое-что новое. Не стесняйтесь добавлять ответ.