ПРИХОДИТЕ УЧИТЬСЯ НОВОЙ ПРОФЕССИИ ЛЕТОМ СО СКИДКОЙ ДО 70%Забронировать скидку

Jackson: вернуть типизированный объект вместо LinkedHashMap

Пройдите тест, узнайте какой профессии подходите и получите бесплатную карьерную консультацию
В конце подарим скидку до 55% на обучение
Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для быстрой десериализации JSON в обобщённый Java-тип с помощью библиотеки Jackson необходимо использовать TypeReference:

Java
Скопировать код
ObjectMapper mapper = new ObjectMapper();
String json = "[{\"name\":\"example\"}]";
TypeReference<List<MyClass<String>>> typeRef = new TypeReference<>() {};
List<MyClass<String>> list = mapper.readValue(json, typeRef);

Анонимный подкласс TypeReference быстро справляется с вопросами типизации, обеспечивая правильную десериализацию для метода readValue.

Пройдите тест и узнайте подходит ли вам сфера IT
Пройти тест

Работа с десериализацией пользовательских обобщённых типов

Рассмотрим проблему десериализации JSON в пользовательские обобщённые типы коллекций в Jackson. Преодолеваем проблему стирания типов в Java, используя JavaType:

Java
Скопировать код
//Не забывайте: возможности JavaType не ограничены!
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class);
List<MyClass> myClasses = mapper.readValue(json, type);

Такой подход с применением JavaType позволяет точно определить обобщённый тип коллекции для Джексона и является более эффективным, нежели использование Class<?>.

Решение задачи со сложными вложенными обобщениями

Для работы со сложной структурой JSON, содержащей вложенные обобщения типа List<Map<String, List<MyClass>>>, воспользуйтесь функциональностью JavaType библиотеки Jackson:

Java
Скопировать код
// Любите JavaType? Добавим JavaType к вашему JavaType!
JavaType innerListType = mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class);
JavaType mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, innerListType);
JavaType outerListType = mapper.getTypeFactory().constructCollectionType(List.class, mapType);
List<Map<String, List<MyClass>>> listOfMaps = mapper.readValue(json, outerListType);

Построение иерархии JavaType позволяет наглядно отражать все уровни обобщённых типов ваших JSON-данных.

Обход скрытия типов с помощью границ

Исчезновение типов в Java может доставить массу хлопот. Однако определение границ типа позволяет избежать превращения T в Object, обеспечивая тем самым чёткую типизацию:

Java
Скопировать код
public class GenericHolder<T extends MyClass> {
    public T value;

    public static <T extends MyClass> GenericHolder<T> fromJson(String json, Class<T> clazz) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        JavaType type = mapper.getTypeFactory().constructParametricType(GenericHolder.class, clazz);
        return mapper.readValue(json, type);
    }
}

Указывая Джексону, что T является подклассом MyClass, мы сохраняем обобщённость кода и гарантируем правильную десериализацию для нужного класса.

Визуализация

Markdown
Скопировать код
Ссылка на Обобщённый Тип🔑: new TypeReference<List<String>>() {}

Без Ключа (TypeRef): `List list = objectMapper.readValue(jsonInput, List.class);`
🔓: [📦📦📦] (🤔 Что там внутри?)

С Ключом (TypeRef): `List<String> list = objectMapper.readValue(jsonInput, new TypeReference<List<String>>() {});`
🔑🔓: [📜📜📜] (✅ Теперь ясно, что наш список содержит строки!)

Владение TypeReference дает нам ключ к разгадке точной информации о обобщённом типе, что способствует правильной десериализации данных Джексоном.

Работа с пользовательскими классами и аннотациями

Пользовательские классы могут решать сложные задачи маршалинга. Правильное применение аннотаций поможет Джексону корректно интерпретировать свойства классов:

Java
Скопировать код
//Добро пожаловать на станцию аннотаций Джексон!
public class Station {
    @JsonProperty("name")
    String stationName;
}

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector());
String jsonInput = "[{\"name\":\"Central Station\"}]";
List<Station> stations = mapper.readValue(jsonInput, new TypeReference<List<Station>>() {});

Декодирование JSON и проверка корректности вывода

JSON-данные могут представлять собой не только String, но и InputStream, который нужно преобразовать:

Java
Скопировать код
//Это — преобразование данных из InputStream в String, а не взлом.
String dataJson = new String(Files.readAllBytes(Paths.get("data.json")), StandardCharsets.UTF_8);
List<Station> stations = mapper.readValue(dataJson, new TypeReference<List<Station>>() {});

stations.forEach(station -> System.out.println(station.getStationName()));

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

Полезные материалы

  1. TypeReference (Jackson-core 2.12.0 API) — здесь вы найдёте подробную информацию о TypeReference.
  2. Java – Constructor annotated with @JsonCreator, why @JsonProperty on arguments? – Stack Overflow — дискуссия на Stack Overflow о важности использования @JsonCreator и @JsonProperty.
  3. Generic Types (The Java™ Tutorials > Learning the Java Language > Generics (Updated)) — углубите знания о обобщённых типах Java с помощью официальных руководств Oracle.
  4. Jackson – Быстрая навигация — полезное руководство по использованию Jackson для работы с JSON в Java.