JavaRush /Курсы /JAVA 25 SELF /Gson — сериализация и десериализация, настройка

Gson — сериализация и десериализация, настройка

JAVA 25 SELF
46 уровень , 2 лекция
Открыта

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-классов (до последних версий)
  • Ограниченная поддержка новых дата-тайп API (например, LocalDate, LocalDateTime — нужны кастомные адаптеры)
  • Не умеет работать с аннотациями Jackson
  • Нет поддержки сложных полиморфных структур «из коробки»
  • Нет автоматической поддержки bidirectional-ссылок (цикличность)

Кастомные адаптеры (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
Простота использования +++++ (очень просто) +++ (чуть сложнее)
Размер библиотеки Маленький Крупнее
Скорость Быстро, но чуть медленнее Очень быстро
Гибкость Средняя Высокая (больше настроек)
Поддержка аннотаций Свои (@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.

1
Задача
JAVA 25 SELF, 46 уровень, 2 лекция
Недоступна
Синхронизация данных о студентах с внешней системой 🎓
Синхронизация данных о студентах с внешней системой 🎓
1
Задача
JAVA 25 SELF, 46 уровень, 2 лекция
Недоступна
Создание публичных профилей в социальной сети 🌐
Создание публичных профилей в социальной сети 🌐
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anton Pohodin Уровень 27
8 ноября 2025
"Мы пока не учили сборку проектов, поэтому для начала можно просто загрузить jar-файл с официальной страницы Gson и добавить его в проект." А слабо сделать то же самое (указать ссылку для скачивания) для Jackson (JSON)?