Все статьи
Руководства

JSON в Kotlin data классы: генерация

Создание Kotlin data classes из JSON, аннотации Gson/Moshi, nullable типы, использование в Android.

21 апреля 2025
7 мин чтения
ConvertHub
#kotlin#json#android

Введение

Kotlin — основной язык разработки под Android и популярный выбор для backend на JVM. Он строготипизирован, лаконичен и поддерживает data class — специальный вид классов для хранения данных с автоматически сгенерированными методамиequals, hashCode иtoString. При работе с REST API почти всегда приходится превращать JSON-ответы в Kotlin-классы. Делать это вручную долго и чревато опечатками в аннотациях. В статье разберём, как Kotlin работает с JSON через Gson, Moshi и kotlinx.serialization, как правильно описывать nullable поля, и как быстро получить готовые классы из примера ответа. Для разовой конвертации используйте нашконвертер JSON в Kotlin data классы.

Что такое data class

Data class объявляется ключевым словом data передclass. В скобках указываются свойства-конструктора, которые участвуют в equals, hashCodeи copy:

data class User(
  val id: Long,
  val name: String,
  val email: String?
)

Этот короткий код заменяет десятки строк Java: геттеры, конструктор, equals, hashCode, toString и copy. Для парсинга JSON достаточно добавить аннотации библиотеки сериализации.

Val против var

В data class принято использовать val — иммутабельные свойства. Это делает объекты потокобезопасными и упрощает рассуждение о состоянии. var оправдан только если библиотека сериализации требует setter (некоторые версии Jackson), либо если объект действительно мутирует в ходе бизнес-логики.

Библиотеки сериализации

Gson

Gson — самая старая и самая распространённая библиотека сериализации на JVM. Не требует аннотаций по умолчанию: имена полей в Kotlin совпадают с ключами JSON. Если имена различаются, используют @SerializedName:

import com.google.gson.annotations.SerializedName

data class User(
  val id: Long,
  @SerializedName("first_name") val firstName: String,
  @SerializedName("is_active") val isActive: Boolean = true
)

Gson не различает null и отсутствие поля, поэтому для опциональных значений используйте nullable-типы (String?) и значения по умолчанию.

Moshi

Moshi — современная альтернатива Gson от Square. Легче, быстрее и нативно понимает Kotlin. Аннотация @Json задаёт имя ключа:

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class User(
  val id: Long,
  @Json(name = "first_name") val firstName: String,
  @Json(name = "is_active") val isActive: Boolean = true
)

Аннотация @JsonClass(generateAdapter = true)включает кодогенерацию: на этапе компиляции создаётся TypeAdapter, что даёт прирост скорости и безопасность относительно рефлексии.

kotlinx.serialization

Официальная библиотека Kotlin, не требует рефлексии и работает в мультиплатформенных проектах (KMP). Аннотации@Serializable и @SerialName:

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class User(
  val id: Long,
  @SerialName("first_name") val firstName: String,
  @SerialName("is_active") val isActive: Boolean = true
)

Для мультиплатформенных проектов это лучший выбор: один и тот же код работает на Android, iOS, в backend и в JavaScript.

Типы данных: соответствие JSON и Kotlin

JSONKotlinПримечание
stringStringUTF-8
number (целое)Int, LongLong для ID и timestamp
number (дробное)DoubleFloat редко
booleanBoolean
objectdata classВложенный класс
arrayList<T>EmptyList по умолчанию
nullT?Nullable-тип
JsonElementДля произвольных данных

Денежные суммы и точность

Для денег Double не подходит — теряется точность. Варианты: Long в копейках/центах, типBigDecimal из Java или специальный@JsonAdapter в Moshi. Генераторы по умолчанию ставят Double — проверяйте и меняйте на подходящий тип для критичных полей.

Nullable поля и значения по умолчанию

В Kotlin String и String? — разные типы. Без знака вопроса переменная не может бытьnull, и библиотека сериализации выбросит исключение, если в JSON пришёл null или поле отсутствует без значения по умолчанию.

data class Profile(
  val login: String,            // обязательно, не null
  val bio: String? = null,      // может отсутствовать или быть null
  val age: Int? = null,         // опциональное число
  val tags: List<String> = emptyList()  // пустой список по умолчанию
)

Правило простое: обязательные поля — без ?, опциональные — с ? и значением по умолчанию. Списки чаще всего инициализируют emptyList(), чтобы не проверять на null в каждой точке использования.

Вложенные объекты и массивы

Вложенные объекты становятся отдельными data class. Если структура встречается только в одном месте — можно объявить её вложенной. Если в нескольких — вынесите в top-level.

data class Order(
  val id: Long,
  val customer: Customer,
  val items: List<Item>,
  val total: Double,
  val createdAt: String        // ISO-8601 timestamp, парсится отдельно
)

data class Customer(
  val id: Long,
  val name: String,
  val email: String? = null
)

data class Item(
  val sku: String,
  val quantity: Int,
  val price: Double
)

Массивы разнотипных объектов

Если в массиве лежат объекты разной формы (например, GraphQL union-типы), применяют sealed class илиJsonElement из kotlinx.serialization. Sealed class удобен для дальнейшей обработки через when:

@Serializable
data class FeedItem(
  val type: String,
  val data: JsonElement
)

// Десериализация конкретного типа:
val post = Json.decodeFromJsonElement<Post>(item.data)

Автоматическая генерация data классов

Ручное переписывание JSON в Kotlin занимает время и оставляет место для ошибок в аннотациях. Современные подходы к генерации — три уровня.

Онлайн-конвертеры

Самый быстрый путь — нашконвертер JSON в Kotlin data классы. Он умеет:

  • определять типы по значениям (Int, Long, Boolean, Double);
  • ставить ? на поля, которые иногда отсутствуют;
  • добавлять значения по умолчанию;
  • генерировать аннотации Gson, Moshi или kotlinx.serialization на выбор;
  • превращать ключи snake_case в camelCase с правильными @SerialName;
  • выносить вложенные объекты в именованные классы.

Плагины для IDE

В Android Studio и IntelliJ IDEA есть плагиныJSON To Kotlin Class (JsonToKotlinClass) иKotlin Data Class File from JSON. Они генерируют классы прямо в проекте, поддерживают шаблоны имён, фильтрацию полей и группировку вложенных объектов в отдельные файлы. Удобно, когда API ещё не стабильно и классы часто пересоздаются.

Генерация из OpenAPI

Если у вас есть OpenAPI-спецификация, используйтеopenapi-generator с флагом--generator-name kotlin илиkotlin-spring для сервера. Это даёт не только модели, но и клиентские интерфейсы — Retrofit или Ktor. На крупных проектах это окупается: меняется API — перегенерация приводит модели в соответствие за секунды.

Практические советы

Имена классов и пакетов

Генераторы часто дают технические имена: AutoGenerated,Response, Item. После генерации переименуйте в осмысленные: UserDto,OrderResponse, OrderItem. СуффиксDto или Response помогает отличать модели данных от доменных сущностей приложения.

DTO и domain models

Не используйте data class из API в бизнес-логике напрямую. Лучше разделить: DTO приходят из сети, mapper превращает их в domain models, с которыми работает UI. Это упрощает тестирование и изолирует изменения API от остального кода.

fun UserDto.toDomain(): User = User(
  id = id,
  displayName = "$firstName $lastName",
  isVerified = isActive
)

Тесты на реальных ответах

Сгенерированные классы обязательно прогоните через десериализацию нескольких реальных ответов API, включая пограничные случаи: пустые массивы, null-поля, отсутствие опциональных ключей. Если библиотека бросаетJsonDataException или MissingKotlinParameterException— добавьте ? или значение по умолчанию.

Десериализация с полиморфизмом

Когда в массиве лежат объекты разного типа, отличающиеся полемtype, применяют sealed class с полиморфной десериализацией. В kotlinx.serialization это решается через аннотацию @Serializable(with = ...) или@JsonClassDiscriminator:

@Serializable
sealed class Notification {
  @Serializable
  @SerialName("email")
  data class Email(val to: String, val subject: String) : Notification()

  @Serializable
  @SerialName("push")
  data class Push(val token: String, val title: String) : Notification()
}

// Десериализация:
val list = Json.decodeFromString<List<Notification>>(json)

После этого можно безопасно обрабатывать список черезwhen и компилятор проверит, что все ветви обработаны. Это удобно для фидов, вебхуков и событий из очередей, где в одном потоке смешаны разные типы сообщений.

Заключение

Kotlin data class — это компактный и безопасный способ представлять данные из JSON. Gson, Moshi и kotlinx.serialization предлагают разные подходы к сериализации, но все они хорошо сочетаются с data class. Nullable-типы и значения по умолчанию помогают корректно обрабатывать отсутствующие поля, вложенные классы — структурировать сложные ответы. Для быстрой генерации классов из примера ответа используйте нашконвертер JSON в Kotlin; для командной работы на крупных проектах — плагины IDE или openapi-generator. Главное правило: после автогенерации переименовывайте классы в осмысленные, добавляйте nullable для опциональных полей и проверяйте на реальных ответах API.

Попробуйте эти инструменты

Похожие статьи