Jackson
Аннотации Jackson для десериализации и изменения структуры JSON
Иногда структура входящего JSON не совпадает напрямую с полями Java-объекта. Для корректной десериализации в таких случаях Jackson предлагает ряд аннотаций, среди которых особенно важны @JsonCreator, @JsonSetter и @JsonAlias.
@JsonCreator
Аннотация @JsonCreator позволяет указать, какой именно конструктор или фабричный метод должен использоваться для создания объекта из JSON.
Особенно полезна в случаях, когда объект должен быть создан через специфический конструктор, а не через пустой и геттеры/сеттеры.
Пример:
При десериализации Jackson использует указанный конструктор, а не требует пустой конструктор и сеттеры:
@JsonSetter
Аннотация @JsonSetter указывается на методе и сообщает Jackson, что данный метод должен использоваться для установки значения при десериализации.
Это полезно, когда метод сеттера отличается по названию от поля или нужно выполнить какую-то дополнительную обработку значения.
Пример:
JSON:
@JsonAlias
Аннотация @JsonAlias позволяет указывать альтернативные имена полей, которые могут приходить в JSON. Это особенно полезно для работы с нестабильными или изменяющимися API.
Пример:
Теперь следующий JSON будет корректно обработан:
или
Пример нестандартного маппинга
Иногда API может менять структуру данных или использовать разные названия для одного и того же поля. Комбинируя @JsonCreator, @JsonProperty и @JsonAlias, можно добиться правильной обработки даже самых нестандартных случаев.
Например:
#Java #Training #Medium #Jackson #JsonCreator #JsonSetter #JsonAlias
Аннотации Jackson для десериализации и изменения структуры JSON
Иногда структура входящего JSON не совпадает напрямую с полями Java-объекта. Для корректной десериализации в таких случаях Jackson предлагает ряд аннотаций, среди которых особенно важны @JsonCreator, @JsonSetter и @JsonAlias.
@JsonCreator
Аннотация @JsonCreator позволяет указать, какой именно конструктор или фабричный метод должен использоваться для создания объекта из JSON.
Особенно полезна в случаях, когда объект должен быть создан через специфический конструктор, а не через пустой и геттеры/сеттеры.
Пример:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
private final String name;
private final int age;
@JsonCreator
public User(@JsonProperty("name") String name,
@JsonProperty("age") int age) {
this.name = name;
this.age = age;
}
// Только геттеры
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
При десериализации Jackson использует указанный конструктор, а не требует пустой конструктор и сеттеры:
String json = "{\"name\":\"Alice\",\"age\":30}";
User user = objectMapper.readValue(json, User.class);
@JsonSetter
Аннотация @JsonSetter указывается на методе и сообщает Jackson, что данный метод должен использоваться для установки значения при десериализации.
Это полезно, когда метод сеттера отличается по названию от поля или нужно выполнить какую-то дополнительную обработку значения.
Пример:
import com.fasterxml.jackson.annotation.JsonSetter;
public class Product {
private String name;
public Product() {}
public String getName() {
return name;
}
@JsonSetter("product_name")
public void setName(String name) {
this.name = name.toUpperCase();
}
}
JSON:
{
"product_name": "laptop"
}
При десериализации будет вызван метод setName, а имя продукта будет автоматически приведено к верхнему регистру: "LAPTOP".
@JsonAlias
Аннотация @JsonAlias позволяет указывать альтернативные имена полей, которые могут приходить в JSON. Это особенно полезно для работы с нестабильными или изменяющимися API.
Пример:
import com.fasterxml.jackson.annotation.JsonAlias;
public class Customer {
@JsonAlias({"user_name", "login"})
private String username;
public Customer() {}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Теперь следующий JSON будет корректно обработан:
{ "user_name": "admin" }
или
{ "login": "admin" }
И в обоих случаях значение попадет в поле username.
Пример нестандартного маппинга
Иногда API может менять структуру данных или использовать разные названия для одного и того же поля. Комбинируя @JsonCreator, @JsonProperty и @JsonAlias, можно добиться правильной обработки даже самых нестандартных случаев.
Например:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonAlias;
public class Employee {
private final String id;
private final String fullName;
@JsonCreator
public Employee(@JsonProperty("id") String id,
@JsonProperty("full_name") @JsonAlias({"name", "fullname"}) String fullName) {
this.id = id;
this.fullName = fullName;
}
public String getId() {
return id;
}
public String getFullName() {
return fullName;
}
}
Теперь Jackson корректно обработает JSON, где имя сотрудника приходит под разными ключами: full_name, name или fullname.
#Java #Training #Medium #Jackson #JsonCreator #JsonSetter #JsonAlias
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
7%
Класс для работы с файлами
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]
53%
[Python, Go]
22%
[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