GsonBuilder — получить объект из интерфейса

У меня есть объявление GsonFactory следующим образом:

public class GsonFactory {
    public Gson create() {
        return new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(MyInterface.class, new MyInstanceCreator())
                .create();
    }

    private static final GsonFactory instance = new GsonFactory();

    private GsonFactory() {
    }

    public static Gson getInstance(){
        return instance.create();
    }
}

У меня также есть класс InstanceCreator:

public class MyInstanceCreator implements InstanceCreator<MyInterface> {
    @Override
    public StrategySymbol createInstance(Type type) {
        return new MyInterfaceImpl();
    }
}

class MyClass {
  private List<MyInterface> myList;

  public List<MyInterface> getMyList() { return myList; }

  public void setMyList(List<MyInterface> myList) { this.myList = myList; }
}

interface MyInterface {
  Long getValue1();
  void setValue1(Long value1);
}

class MyInterfaceImpl implements MyInterface {
  private Long value1;

  @Override
  public Long getValue1() {
     return value1;
  }

  @Override
  public void setValue1(Long value1) {
     this.value1 = value1
  }
}

Этот код кажется хорошо реализованным, но если я попытаюсь разобрать JSON с value1:

MyClass obj = GsonFactory.getInstance().fromJson("{'myList': [{'value1':8}]}", MyClass.class);

Объект возвращается с экземпляром MyInterfaceImpl, но поле value1 всегда пустое. Как я мог видеть, кажется, что Gson ищет поля в interface (нет), а не в class, реализующем interface.

Кто-нибудь знает, как это решить?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
38
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Создатель экземпляра может создавать только экземпляры класса, который не определяет конструктор без аргументов. Он не обрабатывает десериализацию. Гсон по-прежнему не сможет определить конкретный тип данных объекта.

Решение. Пользовательский десериализатор для интерфейса
Вам необходимо определить собственный десериализатор для вашего интерфейса и зарегистрировать этот адаптер нового типа на заводе.

public class MyInterfaceDeserializer implements JsonDeserializer<MyInterface> {
    @Override
    public MyInterface deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        return jsonDeserializationContext.deserialize(jsonElement, MyInterfaceImpl.class);
    }
}

public class GsonFactory {
    public Gson create() {
        return new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(MyInterface.class, new MyInterfaceDeserializer())
                .create();
    }
}

ИЛИ Общий десериализатор

public class InterfaceDeserializer<T, E> implements JsonDeserializer<T> {
    private final Class<E> implementationClass;

    public InterfaceDeserializer(Class<E> implementationClass) {
        this.implementationClass = implementationClass;
    }

    @Override
    public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        return jsonDeserializationContext.deserialize(jsonElement, implementationClass);
    }
}

public class GsonFactory {
    public Gson create() {     
        return new GsonBuilder()
                .setPrettyPrinting()                
               .registerTypeAdapter(MyInterface.class, new InterfaceDeserializer<MyInterface, MyInterfaceImpl>(MyInterfaceImpl.class))
                .create();
}

ИЛИ

public class MyInterfaceDeserializer implements JsonDeserializer<MyInterface> {
    @Override
    public MyInterface deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        MyInterface myInterface = new MyInterfaceImpl();
        JsonObject jObject = jsonElement.getAsJsonObject();
        myInterface.setValue1(jObject.get("value1").getAsLong());
        return myInterface;
    }
}

public class GsonFactory {
    public Gson create() {
        return new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(MyInterface.class, new MyInterfaceDeserializer())
                .create();
    }
}

Другой вариант
Используйте RuntimeTypeAdapterFactory, но для этого решения требуется дополнительное свойство type в json для определения точного подтипа, см. документацию.
Пример:

    public Gson create() {
        RuntimeTypeAdapterFactory<MyInterface> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(MyInterface.class).registerSubtype(MyInterfaceImpl.class, "MyInterfaceImpl");
        return new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
                .create();
    }

JSON должен содержать поле типа:

{
  myList: [
    {
      value1: 8,
      type: MyInterfaceImpl
    }
  ]
}

Обратите внимание, что для этой опции требуется библиотека gson-дополнения. Этот артефакт находится в репозитории Приложение Cron.

Здравствуйте Евгений, спасибо за такой подробный ответ. Конечно, ``CustomDeserializer` - это то, что нужно.

Joe Almore 20.03.2022 04:20

Другие вопросы по теме