Helpers - компьютеры, интернет, программирование

Десериализатор Джексона для универсального типа

Мне нужно написать собственный десериализатор для класса с дженериками. Я не мог найти способ сделать это, однако не могу представить, что я единственный, у кого эта проблема. Насколько я понял, есть два способа реализовать это, но ни один из них не реализуем:

  1. Предоставление параметра класса десериализатору в конструкторе десериализатора не работает, потому что при регистрации десериализатора связь между Type.class и экземпляром десериализатора с переданным экземпляром класса теряется.

Например:

public class Foo<T> {}
public class FooDeserializer<T> {
    public FooDeserializer(Class<T> type) { ... }
    ...
}
// Boilerplate code...
module.addDeserializer(Foo.class, new FooDeserializer<Bar1>(Bar1.class));
module.addDeserializer(Foo.class, new FooDeserializer<Bar2>(Bar2.class));

Это не работает, когда экземпляр ObjectMapper получает экземпляр Foo, информация о типе универсального параметра недоступна (стирание типа), поэтому он просто выбирает последний зарегистрированный десериализатор.

  1. Сохранение ссылки на универсальный тип в классе не помогает, поскольку созданная версия класса не может быть передана десериализатору (интерфейс - readValue (String, Class)).

Например:

String json = "...";
ObjectMapper mapper = ...;
Foo<Bar1> foo = new Foo<>(Bar1.class);
foo = mapper.readValue(json, Foo.class); // Can't pass empty foo instance with Class<?> field containing Bar1.class

Потребуется что-то вроде этого:

mapper.readValue(json, Foo.class, Bar1.class); // Doesn't exist in jackson

Есть предложения, как это сделать?

РЕДАКТИРОВАТЬ: Я нашел способ решить проблему, но это не чистое решение:

Я расширяю FooDeserializer полем Class, чтобы сохранить тип универсального параметра Foo. Затем, каждый раз, когда я хочу десериализовать некоторый json в новый экземпляр Foo, я получаю новый экземпляр ObjectMapper (я использую ObjectMapper # copy на предварительно настроенном экземпляре с фабрики) и передаю ему новый модуль, который содержит экземпляр FooDeserializer с параметр класса (на данный момент я знаю тип). Module, FooDeserializer и копия ObjectMapper недолговечны, они создаются только для этого единственного действия десериализации. Как я уже сказал, не очень чисто, но все же лучше, чем многократно создавать подклассы Foo и писать десериализатор для каждого.

Пример:

public class FooDeserializer<T> extends StdDeserializer<T> {
    private Class<T> type;
    public FooDeserializer(Class<T> type) { this.type = type }
    ...
}

// Meanwhile, before deserialization:
ObjectMapper mapper = MyObjectMapperFactory.get().copy();
SimpleModule module = new SimpleModule(new Version(0,0,1,null,null,null);
module.addDeserializer(Foo.class, new FooDeserializer(Bar1.class);
mapper.addModule(module);
Foo<Bar1> x = mapper.readValue(json, Foo.class); 

Вероятно, поместив это в служебный метод, чтобы скрыть уродство.



Ответы:


1

Я не думаю, что вам нужно писать собственный десериализатор. Вы можете использовать этот синтаксис для десериализации объектов, использующих универсальные шаблоны, взятых из другого потока Stack Overflow.

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});

Пара источников, которые помогут вам: Джексон - десериализация с использованием общего класса http://www.tutorialspoint.com/jackson/jackson_data_binding_generics.htm

08.07.2016
  • Да, в некоторых ситуациях это может сработать. Однако мой тип (Foo в примере) реализует Collection среди нескольких других интерфейсов. Поэтому Джексон использует CollectionDeserializer, а не просто отображает поля класса Foo. Это не работает, потому что CollectionDeserializer ожидает, что json будет начинаться с символа '[', но мой json начинается с символа '{', массив json коллекции вложен в корневой объект json. 09.07.2016

  • 2

    Вам не нужно писать собственный десериализатор. Универсальный можно десериализовать, поместив аннотации Джексона к типу, который передается в качестве параметра универсальному классу.
    Аннотируйте класс Bar1 следующим образом:

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
    @JsonTypeName("Bar1")
    class Bar1 {
    
    }
    

    Теперь, когда вы десериализуете экземпляр Foo<Bar1>, Джексон поместит информацию о параметрах типа в JSON. Затем этот JSON можно десериализовать в общий класс Foo.class, как если бы вы десериализовали любой другой класс.

    ObjectMapper mapper = ...;
    Foo<Bar1> foo = new Foo<Bar1>();
    String json = objectMapper.writeValueAsString(foo);
    foo = mapper.readValue(json, Foo.class); // no need to specify the type Bar1.
    

    Поэтому, если каждый класс, который может быть передан в качестве параметра в Foo, имеет аннотации, тогда JSON может быть десериализован без знания параметра типа во время компиляции.

    См. Документацию Джексона по полиморфной десериализации и Аннотации.

    20.02.2018
    Новые материалы

    Интуитивное понимание тензоров в машинном обучении
    Тензор является важной концепцией во многих научных областях, таких как математика, физика, обработка сигналов и компьютерное зрение, и это лишь некоторые из них. В математике тензор — это..

    Использование машинного обучения для диагностики болезни Альцгеймера, часть 4
    Маркеры семантической согласованности для ранней диагностики болезни Альцгеймера (arXiv) Автор: Давиде Колла , Маттео Дельсанто , Марко Агосто , Бенедетто Витиелло , Даниэле Паоло Радичони..

    Почему объяснимость так важна прямо сейчас?
    По мере того, как системы искусственного интеллекта и инструменты на основе машинного обучения распространяются в нашей повседневной жизни, как практики, так и критики все чаще заявляют о..

    Анимированный математический анализ
    Использование Manim для создания математических анимированных визуализаций Визуализация данных помогает понять скрытые закономерности в данных, которые невозможно визуализировать..

    Создание простого слайдера изображений с помощью JavaScript
    Узнайте, как создать базовый слайдер изображений с помощью HTML, CSS и JavaScript. Введение В этом уроке мы создадим удобный слайдер изображений, используя JavaScript, HTML и CSS. Ползунок..

    Создание базы данных с помощью супергероя «Python»
    В этом посте мы узнаем, как создать «базу данных SQLite с помощью модуля python sqlite3, создав простую функцию входа и регистрации. Готовы ли вы к этому путешествию? Если да , давайте приступим..

    ИИ для чайников: руководство для начинающих по пониманию будущего технологий
    Вы чувствуете, что остались позади в мире ИИ? Не волнуйтесь, вы не одиноки! Со всей этой шумихой вокруг искусственного интеллекта может быть трудно понять, с чего начать. Но не позволяйте сленгу..