Java for Beginner
672 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Jackson

Ключевые аннотации для сериализации.


Jackson предоставляет богатый набор аннотаций для управления тем, как Java-объекты сериализуются в JSON. На практике чаще всего используются три основные аннотации: @JsonProperty, @JsonIgnore и @JsonInclude.

@JsonProperty

Аннотация @JsonProperty позволяет задать имя поля в итоговом JSON независимо от имени переменной в Java-классе. Это особенно полезно, если требуется соблюдать определенные соглашения об именовании в JSON (например, использовать snake_case).

Пример:
import com.fasterxml.jackson.annotation.JsonProperty;

public class User {
@JsonProperty("user_name")
private String name;

private int age;

public User() {}

public User(String name, int age) {
this.name = name;
this.age = age;
}

// Геттеры и сеттеры
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}


При сериализации объект будет выглядеть так:
{"user_name":"Alice","age":25}

Без @JsonProperty имя поля в JSON было бы "name", но аннотация позволила задать собственное имя.


@JsonIgnore

Аннотация @JsonIgnore используется для исключения полей из сериализации и десериализации. Если поле не должно попадать в JSON-вывод или не должно учитываться при чтении JSON, достаточно добавить эту аннотацию.

Пример:
import com.fasterxml.jackson.annotation.JsonIgnore;

public class User {
private String name;

@JsonIgnore
private String password;

public User() {}

public User(String name, String password) {
this.name = name;
this.password = password;
}

// Геттеры и сеттеры
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}


Результат сериализации:
{"name":"Alice"}

Поле password полностью исключено из JSON.


@JsonInclude

Аннотация @JsonInclude позволяет управлять включением полей в JSON в зависимости от их значения. Например, можно настроить сериализацию так, чтобы поля с null не попадали в итоговый JSON.

Пример:
import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private String name;
private String email;

public User() {}

public User(String name, String email) {
this.name = name;
this.email = email;
}

// Геттеры и сеттеры
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}


Если email будет null, результат сериализации будет таким:
{"name":"Alice"}

Без @JsonInclude поле email также попало бы в JSON с null значением.


#Java #Training #Medium #Jackson #JsonProperty #JsonIgnore #JsonInclude
Что выведет код?

import java.util.*;

public class Task050525 {
public static void main(String[] args) {
TreeMap<String, Integer> tree = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
tree.put("Apple", 1);
tree.put("banana", 2);
tree.put("APPLE", 3);

System.out.println(tree.size() + " " + tree.get("aPpLe"));
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
29%
2 1
19%
3 1
35%
2 3
16%
3 3
Не, ну а че, на кулачках решать какой фреймворк лучше - вариант? 🥴 💪 😄

https://t.me/Java_for_beginner_dev

#Mems
Please open Telegram to view this post
VIEW IN TELEGRAM
Jackson

Аннотации 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
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
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
Что выведет код?

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
Варианты ответа:
Anonymous Quiz
10%
A A
50%
C A
23%
C B
17%
B A
Потому что в конец обленимся 😁

https://t.me/Java_for_beginner_dev

#Mems
Please open Telegram to view this post
VIEW IN TELEGRAM
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