W3docs

Java JSON с Gson

Разбор и сериализация JSON в Java с библиотекой Google Gson — toJson, fromJson и TypeToken.

JSON является общепринятым форматом современных веб-API, и Java-приложениям постоянно нужно преобразовывать объекты в JSON и разбирать JSON обратно в объекты. Gson — это open-source библиотека Google именно для этого: небольшой инструментарий без зависимостей, который отображает Java-объекты на JSON-текст и обратно с минимальным количеством шаблонного кода. В этой главе показано, как работает Gson и какие паттерны вы будете использовать каждый день.

Если вы не знакомы с самим форматом, начните со введения в JSON; для основной альтернативной библиотеки см. Java JSON с Jackson.

Добавление Gson в проект

Gson не входит в состав JDK, поэтому его нужно добавить как зависимость. В Maven он объявляется в pom.xml:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.11.0</version>
</dependency>

В Gradle та же зависимость записывается в одну строку:

implementation 'com.google.code.gson:gson:2.11.0'

Всё взаимодействие с Gson происходит через единственный входной класс — Gson. Обычно создают один экземпляр и используют его повторно — он потокобезопасен и не требует значительных ресурсов.

import com.google.gson.Gson;

Gson gson = new Gson();

Сериализация объектов в JSON

Преобразование Java-объекта в JSON-текст называется сериализацией, и Gson выполняет её с помощью toJson(). Вы передаёте любой объект, а Gson обходит его поля через рефлексию, формируя JSON-строку. Для обычных классов аннотации и конфигурация не нужны.

class Book {
    String title;
    String author;
    int year;
    boolean inStock;

    Book(String title, String author, int year, boolean inStock) {
        this.title = title;
        this.author = author;
        this.year = year;
        this.inStock = inStock;
    }
}

Gson gson = new Gson();
Book b = new Book("Clean Code", "Robert Martin", 2008, true);
String json = gson.toJson(b);
// {"title":"Clean Code","author":"Robert Martin","year":2008,"inStock":true}

Имена полей становятся ключами JSON, а Java-типы сопоставляются с естественными JSON-типами: String — в строку в кавычках, int/double — в число, boolean — в true/false. Поле со значением null по умолчанию опускается.

Десериализация JSON в объекты

Обратное направление — десериализация — выполняется с помощью fromJson(). Вы передаёте JSON-текст и целевой Class, а Gson создаёт экземпляр и заполняет поля, сопоставляя ключи JSON с именами полей.

String json = "{\"title\":\"Clean Code\",\"author\":\"Robert Martin\",\"year\":2008,\"inStock\":true}";

Gson gson = new Gson();
Book b = gson.fromJson(json, Book.class);

System.out.println(b.title);  // Clean Code
System.out.println(b.year);   // 2008

Если в JSON есть ключ, которому не соответствует ни одно поле, Gson его игнорирует; если у поля нет соответствующего ключа, оно остаётся со значением по умолчанию (null, 0 или false). Такое снисходительное поведение делает Gson устойчивым при добавлении API новых полей.

TypeToken для обобщённых коллекций

Из-за стирания типов Java удаляет информацию об обобщённых типах во время выполнения, поэтому gson.fromJson(json, List.class) не может знать, что вы хотите получить List<Book> — он вернёт List объектов LinkedTreeMap. Gson решает эту проблему с помощью TypeToken, который захватывает полный обобщённый тип, чтобы десериализация возвращала нужные элементы.

import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;

String json = "[{\"title\":\"Effective Java\",\"year\":2018}]";

Type listType = new TypeToken<List<Book>>(){}.getType();
List<Book> books = gson.fromJson(json, listType);

Используйте TypeToken всякий раз, когда целевой тип включает обобщения — List<T>, Map<K, V> или вложенные комбинации. Для простых необобщённых классов достаточно формы Book.class.

Красивый вывод и пользовательская конфигурация

По умолчанию Gson формирует компактный однострочный JSON. Для настройки поведения используйте GsonBuilder. Наиболее частый запрос — pretty printing — читаемый, с отступами вывод для логов и конфигурационных файлов.

import com.google.gson.GsonBuilder;

Gson gson = new GsonBuilder()
        .setPrettyPrinting()
        .serializeNulls()        // include null fields instead of dropping them
        .create();

System.out.println(gson.toJson(b));

GsonBuilder управляет многими другими настройками. Вот те, что встречаются чаще всего:

Метод builderЭффект
setPrettyPrinting()Вывод с отступами в несколько строк
serializeNulls()Включать поля null вместо их пропуска
setDateFormat(...)Управлять форматированием значений Date
registerTypeAdapter(...)Подключить пользовательский сериализатор/десериализатор для типа
excludeFieldsWithoutExposeAnnotation()Сериализовать только поля, помеченные @Expose

Для полного контроля над одним типом регистрируется пользовательский адаптер, реализующий JsonSerializer и/или JsonDeserializer — полезно, когда поле требует специального форматирования, которое рефлексия не может определить.

Практический пример: сериализация, разбор и цикл туда-обратно

Gson недоступен в окружении этого раннера, поэтому приведённая ниже программа демонстрирует те же концепции, используя только JDK: она вручную сериализует запись в JSON, разбирает текст обратно на поля, воссоздаёт объект и подтверждает, что цикл преобразования без потерь. Именно это автоматизируют для вас gson.toJson() и gson.fromJson().

java— editable, runs on the server

Что можно извлечь из запуска:

  • Первая строка показывает сериализацию: запись Book превращается в {"title":"Clean Code","author":"Robert Martin","year":2008,"inStock":true}, где каждое поле — это ключ JSON, а типы сопоставлены с их естественными JSON-формами — именно это производит gson.toJson().
  • Вторая строка показывает десериализацию: разбор этого текста и воссоздание Book — ту же работу, что gson.fromJson(json, Book.class) выполняет за один вызов.
  • Round-trip equal? true подтверждает, что преобразование без потерь — сериализация с последующей десериализацией возвращает объект, равный оригиналу, что является свойством, к которому стремится каждый JSON-маппер.
  • Строка Array JSON показывает List<Book>, отрисованный как массив объектов JSON — структуру, которую вы будете десериализовывать с помощью TypeToken<List<Book>>.
  • Блок Pretty показывает вывод с отступами в несколько строк, иллюстрируя то, что даёт GsonBuilder.setPrettyPrinting() по сравнению с компактным вариантом по умолчанию.

Практика

Практика
В Gson, почему для десериализации List<Book> нужен TypeToken, а не просто List.class?
В Gson, почему для десериализации List<Book> нужен TypeToken, а не просто List.class?
Was this page helpful?