Jackson
Работа со списками, картами и вложенными объектами
JSON — это не только простые объекты, но и сложные структуры: списки, словари, вложенные объекты. В этом посте разберем, как Jackson справляется с такими случаями, и как правильно сериализовать и десериализовать коллекции и составные структуры.
Списки объектов
Представим, что у нас есть список пользователей:
Сериализация списка в JSON:
Результат:
Десериализация списка обратно в Java:
Карты (мапы)
JSON-объект с произвольными ключами часто удобно мапить в Map<String, Object>.
Например:
Можно прочитать в Map:
Сериализация карты обратно:
Вложенные объекты
Вложенные структуры — это JSON, где одно поле содержит объект:
Соответствующий Java-класс:
Jackson автоматически корректно сериализует и десериализует такие вложенные объекты:
Результат:
И обратно:
Комбинирование структур
Jackson легко справляется с комбинацией вложенных объектов, списков и мап:
Нужно лишь правильно описать классы:
#Java #Training #Medium #Jackson
Работа со списками, картами и вложенными объектами
JSON — это не только простые объекты, но и сложные структуры: списки, словари, вложенные объекты. В этом посте разберем, как Jackson справляется с такими случаями, и как правильно сериализовать и десериализовать коллекции и составные структуры.
Списки объектов
Представим, что у нас есть список пользователей:
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Геттеры и сеттеры
}
Сериализация списка в JSON:
List<User> users = List.of(
new User("Alice", 30),
new User("Bob", 25)
);
String json = objectMapper.writeValueAsString(users);
System.out.println(json);
Результат:
[
{"name":"Alice","age":30},
{"name":"Bob","age":25}
]
Десериализация списка обратно в Java:
String jsonInput = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
List<User> users = objectMapper.readValue(
jsonInput,
new TypeReference<List<User>>() {}
);
Здесь важно использовать TypeReference, чтобы сохранить информацию о типе во время десериализации.
Карты (мапы)
JSON-объект с произвольными ключами часто удобно мапить в Map<String, Object>.
Например:
{
"name": "Alice",
"age": 30,
"active": true
}
Можно прочитать в Map:
String json = "{\"name\":\"Alice\",\"age\":30,\"active\":true}";
Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
Сериализация карты обратно:
Map<String, Object> data = new HashMap<>();
data.put("name", "Alice");
data.put("age", 30);
data.put("active", true);
String jsonOutput = objectMapper.writeValueAsString(data);
Вложенные объекты
Вложенные структуры — это JSON, где одно поле содержит объект:
{
"id": 1,
"user": {
"name": "Alice",
"age": 30
}
}
Соответствующий Java-класс:
public class Wrapper {
private int id;
private User user;
public Wrapper() {}
public Wrapper(int id, User user) {
this.id = id;
this.user = user;
}
// Геттеры и сеттеры
}
Jackson автоматически корректно сериализует и десериализует такие вложенные объекты:
Wrapper wrapper = new Wrapper(1, new User("Alice", 30));
String json = objectMapper.writeValueAsString(wrapper);
Результат:
{
"id": 1,
"user": {
"name": "Alice",
"age": 30
}
}
И обратно:
String input = "{\"id\":1,\"user\":{\"name\":\"Alice\",\"age\":30}}";
Wrapper wrapper = objectMapper.readValue(input, Wrapper.class);
Комбинирование структур
Jackson легко справляется с комбинацией вложенных объектов, списков и мап:
{
"group": "admins",
"members": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
],
"meta": {
"created": "2023-10-01",
"active": true
}
}
Нужно лишь правильно описать классы:
public class Group {
private String group;
private List<User> members;
private Map<String, Object> meta;
// Конструктор, геттеры, сеттеры
}
#Java #Training #Medium #Jackson
Что выведет код?
#Tasks
import java.util.*;
public class Task060525 {
public static void main(String[] args) {
TreeSet<String> tree = new TreeSet<>(Comparator.reverseOrder());
tree.add("B");
tree.add("A");
tree.add("C");
tree.add("A");
System.out.println(tree.pollFirst() + " " + tree.pollLast());
}
}
#Tasks
Please open Telegram to view this post
VIEW IN TELEGRAM
Вопросы с собеседования 👩💻
Что такое module в Java?
Что такое module в Java?
Anonymous Quiz
8%
Класс для работы с файлами
88%
Единица модульной организации кода
0%
Тип данных для чисел
5%
Метод для обработки исключений
Jackson
Кастомные десериализаторы
Иногда структура входящего JSON или требования к обработке данных слишком специфичны, и стандартной десериализации Jackson оказывается недостаточно. В таких случаях можно реализовать собственный десериализатор, унаследовавшись от JsonDeserializer.
Когда нужен кастомный десериализатор
— Нужно преобразовать нестандартный формат данных
— Нужно добавить проверку, валидацию, нормализацию
— Нужно маппить одно поле в несколько
— Нужно логировать, фильтровать, трансформировать содержимое JSON
Шаг 1: Создание собственного десериализатора
Для этого создаем класс, расширяющий JsonDeserializer<T>:
Шаг 2: Привязка десериализатора к полю
Теперь мы можем использовать наш кастомный десериализатор на нужном поле через аннотацию @JsonDeserialize.
Шаг 3: Пример использования
Что ещё можно делать в кастомных десериализаторах
— Преобразовывать дату из нестандартного формата
— Разбирать строки с разделителями (например, CSV в List)
— Валидировать числовые диапазоны
— Преобразовывать строки в enum с логикой по умолчанию
— Обрабатывать null как спецзначения
— Инъектировать зависимости (через context.findInjectableValue())
#Java #Training #Medium #Jackson
Кастомные десериализаторы
Иногда структура входящего JSON или требования к обработке данных слишком специфичны, и стандартной десериализации Jackson оказывается недостаточно. В таких случаях можно реализовать собственный десериализатор, унаследовавшись от JsonDeserializer.
Когда нужен кастомный десериализатор
— Нужно преобразовать нестандартный формат данных
— Нужно добавить проверку, валидацию, нормализацию
— Нужно маппить одно поле в несколько
— Нужно логировать, фильтровать, трансформировать содержимое JSON
Шаг 1: Создание собственного десериализатора
Для этого создаем класс, расширяющий JsonDeserializer<T>:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
public class NameDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String rawValue = p.getText();
if (rawValue == null || rawValue.trim().isEmpty()) {
throw new IOException("Имя не может быть пустым");
}
// Пример нормализации: первая буква заглавная
return rawValue.substring(0, 1).toUpperCase() + rawValue.substring(1).toLowerCase();
}
}
Шаг 2: Привязка десериализатора к полю
Теперь мы можем использовать наш кастомный десериализатор на нужном поле через аннотацию @JsonDeserialize.
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
public class User {
@JsonDeserialize(using = NameDeserializer.class)
private String name;
private int age;
public User() {}
// Геттеры и сеттеры
}
Шаг 3: Пример использования
String json = "{\"name\":\" aLiCe \", \"age\":30}";
User user = objectMapper.readValue(json, User.class);
System.out.println(user.getName()); // Выведет: Alice
Если в поле name будет пустая строка или только пробелы — Jackson выбросит исключение, как мы это настроили в десериализаторе.
Что ещё можно делать в кастомных десериализаторах
— Преобразовывать дату из нестандартного формата
— Разбирать строки с разделителями (например, CSV в List)
— Валидировать числовые диапазоны
— Преобразовывать строки в enum с логикой по умолчанию
— Обрабатывать null как спецзначения
— Инъектировать зависимости (через context.findInjectableValue())
#Java #Training #Medium #Jackson
Jackson
Глобальная настройка ObjectMapper: как управлять сериализацией и десериализацией
Jackson предоставляет удобный способ централизованно настраивать поведение через ObjectMapper. Эта конфигурация позволяет управлять форматированием JSON, обработкой неизвестных полей, null-значений и множеством других аспектов.
Инициализация ObjectMapper
1. Игнорирование неизвестных полей
По умолчанию Jackson выбросит исключение, если в JSON попадётся поле, которого нет в Java-классе.
Это можно отключить так:
Теперь лишние поля будут просто игнорироваться:
2. Красивая печать JSON (Pretty Print)
По умолчанию JSON сериализуется в одну строку. Чтобы включить форматирование с отступами:
Пример вывода:
3. Исключение null-полей
Если нужно убрать из JSON все поля с null, можно настроить так:
Пример:
Результат без email, если он null:
4. Ошибка при отсутствии обязательных полей
По умолчанию Jackson пропускает отсутствующие поля. Но можно требовать их наличия:
5. Написание чисел как строки
Результат:
6. Установка глобального формата дат
Когда это полезно
— В проектах с нестабильными API
— При интеграции с фронтендом, где важно форматирование
— При логировании JSON в читаемом виде
— Для обеспечения устойчивости к изменяющимся данным
#Java #Training #Medium #Jackson #ObjectMapper
Глобальная настройка ObjectMapper: как управлять сериализацией и десериализацией
Jackson предоставляет удобный способ централизованно настраивать поведение через ObjectMapper. Эта конфигурация позволяет управлять форматированием JSON, обработкой неизвестных полей, null-значений и множеством других аспектов.
Инициализация ObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
Можно использовать его напрямую или создать бин (например, в Spring).
1. Игнорирование неизвестных полей
По умолчанию Jackson выбросит исключение, если в JSON попадётся поле, которого нет в Java-классе.
Это можно отключить так:
import com.fasterxml.jackson.databind.DeserializationFeature;
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Теперь лишние поля будут просто игнорироваться:
{
"name": "Alice",
"age": 30,
"extra_field": "ignored"
}
2. Красивая печать JSON (Pretty Print)
По умолчанию JSON сериализуется в одну строку. Чтобы включить форматирование с отступами:
import com.fasterxml.jackson.databind.SerializationFeature;
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Пример вывода:
{
"name" : "Alice",
"age" : 30
}
Это особенно полезно для логирования и отладки.
3. Исключение null-полей
Если нужно убрать из JSON все поля с null, можно настроить так:
import com.fasterxml.jackson.annotation.JsonInclude;
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Пример:
public class User {
public String name;
public String email; // может быть null
}
Результат без email, если он null:
{
"name": "Alice"
}
4. Ошибка при отсутствии обязательных полей
По умолчанию Jackson пропускает отсутствующие поля. Но можно требовать их наличия:
mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true);
5. Написание чисел как строки
mapper.configure(SerializationFeature.WRITE_NUMBERS_AS_STRINGS, true);
Результат:
{
"age": "30"
}
6. Установка глобального формата дат
import java.text.SimpleDateFormat;
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
Когда это полезно
— В проектах с нестабильными API
— При интеграции с фронтендом, где важно форматирование
— При логировании JSON в читаемом виде
— Для обеспечения устойчивости к изменяющимся данным
#Java #Training #Medium #Jackson #ObjectMapper
Что выведет код?
#Tasks
import java.util.*;
public class Task070525 {
public static void main(String[] args) {
TreeMap<Integer, String> tree = new TreeMap<>();
tree.put(1, "Java");
tree.put(1, "Python");
tree.put(2, "C++");
tree.put(3, "Go");
tree.remove(2);
System.out.println(tree.subMap(1, true, 3, true).values());
}
}
#Tasks
Варианты ответа:
Anonymous Quiz
14%
[Python]
51%
[Python, Go]
23%
[Java, Python, Go]
11%
[Python, C++, Go]
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Вопросы с собеседования 👩💻
Что делает метод strip() в классе String?
Что делает метод strip() в классе String?
Anonymous Quiz
47%
Удаляет пробелы и управляющие символы в начале и конце строки
35%
Удаляет все пробелы
3%
Преобразует строку в верхний регистр
15%
Разделяет строку на части
Jackson
Использование модулей в Jackson: JavaTimeModule и работа с Java 8 датами
Начиная с Java 8, в язык была добавлена новая мощная модель работы со временем: LocalDate, LocalDateTime, Instant и другие типы из пакета java.time. Однако стандартная конфигурация Jackson не умеет корректно сериализовать и десериализовать эти типы "из коробки".
Проблема по умолчанию
Если попытаться сериализовать объект с полем LocalDateTime, Jackson выдаст ошибку или выведет нечитабельное представление (например, через timestamp).
Пример:
Решение: JavaTimeModule
Чтобы Jackson начал понимать java.time.*, нужно подключить модуль JavaTimeModule.
Подключение (если используешь Maven):
Регистрация модуля:
Важно: если хочешь сериализовать даты в человекочитаемом формате, отключи запись timestamp:
Пример работы
Результат:
Аналогично работает и с другими типами:
LocalDate
LocalTime
ZonedDateTime
Instant
Десериализация обратно
Другие полезные модули Jackson
ParameterNamesModule — помогает при десериализации через конструкторы без @JsonProperty
Jdk8Module — поддержка Optional, OptionalInt и других типов из java.util
AfterburnerModule — ускоряет работу Jackson на больших объёмах данных
#Java #Training #Medium #Jackson
Использование модулей в Jackson: JavaTimeModule и работа с Java 8 датами
Начиная с Java 8, в язык была добавлена новая мощная модель работы со временем: LocalDate, LocalDateTime, Instant и другие типы из пакета java.time. Однако стандартная конфигурация Jackson не умеет корректно сериализовать и десериализовать эти типы "из коробки".
Проблема по умолчанию
Если попытаться сериализовать объект с полем LocalDateTime, Jackson выдаст ошибку или выведет нечитабельное представление (например, через timestamp).
Пример:
public class Event {
public String name;
public LocalDateTime dateTime;
public Event() {}
public Event(String name, LocalDateTime dateTime) {
this.name = name;
this.dateTime = dateTime;
}
}
Event event = new Event("Conference", LocalDateTime.now());
String json = objectMapper.writeValueAsString(event);
Без дополнительных модулей Jackson не поймёт, как сериализовать LocalDateTime.
Решение: JavaTimeModule
Чтобы Jackson начал понимать java.time.*, нужно подключить модуль JavaTimeModule.
Подключение (если используешь Maven):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Регистрация модуля:
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
Важно: если хочешь сериализовать даты в человекочитаемом формате, отключи запись timestamp:
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
Пример работы
Event event = new Event("Conference", LocalDateTime.of(2025, 4, 29, 10, 30));
String json = mapper.writeValueAsString(event);
System.out.println(json);
Результат:
{
"name": "Conference",
"dateTime": "2025-04-29T10:30:00"
}
Аналогично работает и с другими типами:
LocalDate
LocalTime
ZonedDateTime
Instant
Десериализация обратно
String input = "{\"name\":\"Conference\",\"dateTime\":\"2025-04-29T10:30:00\"}";
Event event = mapper.readValue(input, Event.class);
Другие полезные модули Jackson
ParameterNamesModule — помогает при десериализации через конструкторы без @JsonProperty
Jdk8Module — поддержка Optional, OptionalInt и других типов из java.util
AfterburnerModule — ускоряет работу Jackson на больших объёмах данных
#Java #Training #Medium #Jackson
Jackson
Частичная сериализация с помощью @JsonView
Иногда возникает задача сериализовать один и тот же объект по-разному — в зависимости от ситуации, роли пользователя, уровня доступа или цели запроса. Например, показывать часть полей для публичного API и все — для администратора.
Jackson решает эту задачу с помощью механизма JSON Views.
Что такое JSON Views
@JsonView — это аннотация, позволяющая управлять, какие поля объекта попадут в JSON в зависимости от выбранного представления (view-класса).
Это как "фильтр по уровням доступа", встроенный прямо в сериализацию.
Шаг 1. Определяем уровни видимости (view)
Шаг 2. Аннотируем поля модели
Теперь:
username виден всем (включая Public)
email и role — только для Internal
Шаг 3. Сериализация с учетом view
Результат:
Если сериализовать с Internal view:
Результат:
Применение в REST (например, Spring)
В Spring можно использовать @JsonView прямо в контроллере:
Когда это нужно
Публичный/приватный API
Разные роли пользователей (гость, клиент, админ)
Безопасность: не передавать чувствительные поля
Удобство: не создавать множество DTO для каждой ситуации
#Java #Training #Medium #Jackson #JsonView
Частичная сериализация с помощью @JsonView
Иногда возникает задача сериализовать один и тот же объект по-разному — в зависимости от ситуации, роли пользователя, уровня доступа или цели запроса. Например, показывать часть полей для публичного API и все — для администратора.
Jackson решает эту задачу с помощью механизма JSON Views.
Что такое JSON Views
@JsonView — это аннотация, позволяющая управлять, какие поля объекта попадут в JSON в зависимости от выбранного представления (view-класса).
Это как "фильтр по уровням доступа", встроенный прямо в сериализацию.
Шаг 1. Определяем уровни видимости (view)
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
Можно создавать любые уровни — главное, чтобы они наследовались при необходимости.
Шаг 2. Аннотируем поля модели
import com.fasterxml.jackson.annotation.JsonView;
public class User {
@JsonView(Views.Public.class)
private String username;
@JsonView(Views.Internal.class)
private String email;
@JsonView(Views.Internal.class)
private String role;
public User() {}
public User(String username, String email, String role) {
this.username = username;
this.email = email;
this.role = role;
}
// Геттеры и сеттеры
}
Теперь:
username виден всем (включая Public)
email и role — только для Internal
Шаг 3. Сериализация с учетом view
ObjectMapper mapper = new ObjectMapper();
User user = new User("alice", "alice@example.com", "ADMIN");
// Публичный вид
String publicJson = mapper
.writerWithView(Views.Public.class)
.writeValueAsString(user);
System.out.println(publicJson);
Результат:
{
"username": "alice"
}
Если сериализовать с Internal view:
String internalJson = mapper
.writerWithView(Views.Internal.class)
.writeValueAsString(user);
Результат:
{
"username": "alice",
"email": "alice@example.com",
"role": "ADMIN"
}
Применение в REST (например, Spring)
В Spring можно использовать @JsonView прямо в контроллере:
@GetMapping("/user/public")
@JsonView(Views.Public.class)
public User getPublicUser() {
return userService.getCurrentUser();
}
@GetMapping("/user/internal")
@JsonView(Views.Internal.class)
public User getInternalUser() {
return userService.getCurrentUser();
}
Когда это нужно
Публичный/приватный API
Разные роли пользователей (гость, клиент, админ)
Безопасность: не передавать чувствительные поля
Удобство: не создавать множество DTO для каждой ситуации
#Java #Training #Medium #Jackson #JsonView