1. Вступ до Gson
Ми вже познайомилися з Jackson і побачили, чому він вважається стандартом де-факто для роботи з JSON у Java. Але є й інша бібліотека, яка здобула величезну популярність, особливо у світі Android, — це Gson. Gson створювався в Google як легке й просте рішення для серіалізації та десеріалізації об’єктів Java у JSON. Його цінують за мінімальний поріг входу: щоб почати працювати, майже нічого не потрібно налаштовувати — більшість завдань вирішується буквально «з коробки».
Ще одна перевага Gson полягає в його легкості. Бібліотека займає мало місця і не тягне за собою безліч залежностей, тому її часто використовують там, де критичний розмір застосунку, наприклад, на мобільних пристроях. Gson став фактичним стандартом для Android‑проєктів — компактність і простота відіграють там вирішальну роль.
До речі, назва Gson розшифровується як Google JSON. Іноді в спільноті можна зустріти жартівливу розшифровку — Genius’ Son («син генія»), але це, звісно, неофіційно. Просто гра слів.
Підключення Gson до проєкту
Якщо ви використовуєте Maven або Gradle, просто додайте залежність (версія може відрізнятися):
Maven:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
Gradle:
implementation 'com.google.code.gson:gson:2.10.1'
Ми поки що не вивчали збирання проєктів, тому для початку можна просто завантажити jar-файл з офіційної сторінки Gson і додати його у проєкт.
2. Базові операції: серіалізація та десеріалізація
Подивімося, як серіалізувати й десеріалізувати об’єкти за допомогою Gson на прикладі простого класу.
Приклад: клас User
// Клас для прикладів
public class User {
private String name;
private int age;
private boolean active;
// Конструктор
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// Гетери й сетери (Gson використовує їх за потреби)
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
}
Серіалізація: об’єкт → JSON
import com.google.gson.Gson;
public class GsonExample {
public static void main(String[] args) {
User user = new User("Alice", 25, true);
Gson gson = new Gson();
String json = gson.toJson(user);
System.out.println(json);
// {"name":"Alice","age":25,"active":true}
}
}
Зверніть увагу: поля серіалізуються з їхніми іменами, заданими в класі!
Десеріалізація: JSON → об’єкт
public class GsonExample {
public static void main(String[] args) {
String json = "{\"name\":\"Bob\",\"age\":30,\"active\":false}";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
System.out.println(user.getName()); // Bob
System.out.println(user.getAge()); // 30
System.out.println(user.isActive());// false
}
}
Робота зі списками об’єктів
Gson працює з колекціями трохи складніше, ніж Jackson, але усе розв’язується.
import java.util.List;
import java.util.Arrays;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
public class GsonListExample {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 30, false)
);
Gson gson = new Gson();
String json = gson.toJson(users);
System.out.println(json);
// [{"name":"Alice","age":25,"active":true},{"name":"Bob","age":30,"active":false}]
// Десеріалізація списку
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users2 = gson.fromJson(json, userListType);
System.out.println(users2.get(0).getName()); // Alice
}
}
Важливий нюанс: для десеріалізації колекцій використовуйте TypeToken<>!
3. Налаштування Gson: GsonBuilder
Gson надає гнучке налаштування через клас GsonBuilder. З його допомогою ви можете ввімкнути pretty printing, серіалізацію значень null, форматування дат і багато іншого.
Приклад: pretty printing і серіалізація null
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonBuilderExample {
public static void main(String[] args) {
User user = new User("Charlie", 0, false);
Gson gson = new GsonBuilder()
.setPrettyPrinting() // Гарне виведення (відступи)
.serializeNulls() // Серіалізувати null‑поля
.create();
String json = gson.toJson(user);
System.out.println(json);
/*
{
"name": "Charlie",
"age": 0,
"active": false
}
*/
}
}
Форматування дат
Якщо у вас є поля типу Date, за замовчуванням Gson серіалізує їх у специфічному форматі. Можна задати власний формат:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Date;
public class DateExample {
private String event;
private Date date;
public DateExample(String event, Date date) {
this.event = event;
this.date = date;
}
}
public class Main {
public static void main(String[] args) {
DateExample meeting = new DateExample("Team Meeting", new Date());
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
String json = gson.toJson(meeting);
System.out.println(json);
// {"event":"Team Meeting","date":"2024-06-10 13:45:23"}
}
}
4. Анотації Gson: керування серіалізацією
Gson підтримує анотації для точнішого контролю над серіалізацією та десеріалізацією.
@SerializedName — перейменування поля
Якщо ви хочете, щоб поле в JSON називалося інакше, використовуйте @SerializedName:
import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("full_name")
private String name;
private int age;
private boolean active;
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Тепер під час серіалізації поле матиме назву full_name:
User user = new User("Diana", 28, true);
String json = new Gson().toJson(user);
// {"full_name":"Diana","age":28,"active":true}
@Expose — серіалізація лише позначених полів
Якщо ви хочете серіалізувати лише певні поля, використовуйте @Expose і налаштуйте Gson:
import com.google.gson.annotations.Expose;
public class User {
@Expose
private String name;
@Expose
private int age;
private boolean active; // не серіалізується
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Створюємо Gson з підтримкою @Expose:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
User user = new User("Eve", 21, false);
String json = gson.toJson(user);
// {"name":"Eve","age":21}
@Since/@Until — умовна серіалізація за версією
Можна використовувати @Since і @Until, щоб серіалізувати поля лише для певних версій (рідко використовується на практиці, але знати корисно).
5. Особливості та обмеження Gson
Робота з вкладеними об’єктами
Gson чудово працює з вкладеними об’єктами:
public class Profile {
private User user;
private String bio;
public Profile(User user, String bio) {
this.user = user;
this.bio = bio;
}
}
Profile profile = new Profile(new User("Frank", 27, true), "Java developer");
String json = new Gson().toJson(profile);
// {"user":{"name":"Frank","age":27,"active":true},"bio":"Java developer"}
Робота з колекціями
З серіалізацією колекцій (List, Map) проблем немає, але для десеріалізації використовуйте TypeToken (див. вище).
Обмеження Gson порівняно з Jackson
- Немає підтримки Java record-класів (до останніх версій)
- Обмежена підтримка нового date/time API (наприклад, LocalDate, LocalDateTime — потрібні кастомні адаптери)
- Не вміє працювати з анотаціями Jackson
- Немає підтримки складних поліморфних структур «з коробки»
- Немає автоматичної підтримки двонапрямлених посилань (циклічних посилань)
Кастомні адаптери (TypeAdapter)
Якщо стандартних можливостей замало, можна написати власний адаптер для серіалізації/десеріалізації складних типів.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class BooleanAsIntAdapter extends TypeAdapter<Boolean> {
@Override
public void write(JsonWriter out, Boolean value) throws IOException {
out.value(value ? 1 : 0);
}
@Override
public Boolean read(JsonReader in) throws IOException {
return in.nextInt() == 1;
}
}
// Використання:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.create();
6. Практика: серіалізація й десеріалізація з налаштуваннями
Розвиньмо ваш навчальний застосунок і додаймо збереження та завантаження списку користувачів у форматі JSON.
Клас User з анотаціями
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;
public class User {
@Expose
@SerializedName("full_name")
private String name;
@Expose
private int age;
private boolean active; // не серіалізується
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// гетери й сетери…
}
Зберігаємо список користувачів у JSON
import java.util.List;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class SaveUsers {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Ivan", 23, true),
new User("Olga", 19, false)
);
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create();
String json = gson.toJson(users);
System.out.println(json);
/*
[
{
"full_name": "Ivan",
"age": 23
},
{
"full_name": "Olga",
"age": 19
}
]
*/
}
}
Завантажуємо список користувачів із JSON
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
public class LoadUsers {
public static void main(String[] args) {
String json = "[{\"full_name\":\"Ivan\",\"age\":23},{\"full_name\":\"Olga\",\"age\":19}]";
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users = gson.fromJson(json, userListType);
for (User user : users) {
System.out.println(user.getName() + " (" + user.getAge() + ")");
}
// Ivan (23)
// Olga (19)
}
}
7. Порівняння Gson і Jackson
| Характеристика | Gson | Jackson |
|---|---|---|
| Простота використання | +++++ (дуже просто) | +++ (трохи складніше) |
| Розмір бібліотеки | Невеликий | Більший |
| Швидкість | Швидко, але трохи повільніше за Jackson | Дуже швидко |
| Гнучкість | Середня | Висока (більше налаштувань) |
| Підтримка анотацій | Власні (@SerializedName) | Власні (@JsonProperty тощо) |
| Підтримка нових типів | Обмежена | Відмінна (Java 8+, record) |
| Підтримка Android | Відмінна | Хороша, але важча |
| Робота з датами | Лише через адаптери | Із коробки |
| Поліморфізм | Обмежений | Гнучко налаштовується |
8. Типові помилки під час роботи з Gson
Помилка № 1: не використовуєте TypeToken для колекцій.
Якщо десеріалізуєте список або мапу, обов’язково використовуйте TypeToken<>, інакше отримаєте дивні помилки або порожні колекції.
Помилка № 2: немає конструктора без параметрів.
Gson може працювати і без конструктора за замовчуванням, але іноді під час десеріалізації складних об’єктів без такого конструктора можуть виникати помилки. Краще завжди додавати конструктор без параметрів, якщо плануєте десеріалізацію.
Помилка № 3: невідповідність імен полів.
Якщо в JSON поле називається "full_name", а в класі — "name", без анотації @SerializedName("full_name") поле не буде зіставлене, і значення буде null.
Помилка № 4: проблеми з приватними полями.
Gson може серіалізувати приватні поля, але якщо є лише приватні поля й немає гетерів/сетерів, іноді виникають проблеми під час десеріалізації. Краще використовувати гетери й сетери.
Помилка № 5: робота з датами.
За замовчуванням Gson серіалізує Date у незручному форматі. Для LocalDate, LocalDateTime та інших нових типів без кастомних адаптерів будуть помилки серіалізації.
Помилка № 6: не використовуєте @Expose, але увімкнули excludeFieldsWithoutExposeAnnotation().
Якщо ви увімкнули excludeFieldsWithoutExposeAnnotation(), але не позначили поля анотацією @Expose, вони не серіалізуватимуться й не десеріалізуватимуться — результатом буде порожній JSON або об’єкти з null.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ