Введение: почему JSON — основа современного Kotlin-разработчика
JSON (JavaScript Object Notation) остаётся стандартом де-факто для обмена данными между сервером и клиентом, будь то мобильное приложение на Android или бэкенд на Ktor. В экосистеме Kotlin работа с JSON особенно удобна благодаря строгой типизации языка и мощным библиотекам сериализации. В этой статье мы разберём два основных подхода: использование официальной библиотеки kotlinx.serialization и популярной Gson. Вы узнаете, как парсить сложные структуры, сериализовать объекты и обрабатывать исключения на практике.
Подготовка проекта и подключение зависимостей
Прежде чем писать код, необходимо добавить библиотеки в файл build.gradle.kts (для Gradle). Рассмотрим оба варианта.
Вариант 1: kotlinx.serialization (рекомендуемый)
- Добавьте плагин в
plugins:kotlin("plugin.serialization") version "1.9.22" - В
dependenciesукажите:implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
Вариант 2: Gson (для совместимости с Java-проектами)
- В
dependenciesдобавьте:implementation("com.google.code.gson:gson:2.10.1")
// build.gradle.kts — полный пример для kotlinx.serialization
plugins {
kotlin("jvm") version "1.9.22"
kotlin("plugin.serialization") version "1.9.22"
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
Парсинг JSON в Kotlin: от простого к сложному
Парсинг (десериализация) — это преобразование JSON-строки в объект Kotlin. Рассмотрим на примере данных о пользователе.
Пример данных
{
"id": 123,
"name": "Анна",
"email": "anna@example.com",
"active": true
}
Создание Data Class
Для обоих библиотек используется один и тот же класс. Важно: для kotlinx.serialization нужно добавить аннотацию @Serializable.
import kotlinx.serialization.Serializable
@Serializable
data class User(
val id: Int,
val name: String,
val email: String,
val active: Boolean
)
Парсинг с помощью kotlinx.serialization
import kotlinx.serialization.json.Json
fun parseWithKotlinx(jsonString: String): User {
val json = Json { ignoreUnknownKeys = true } // игнорируем неизвестные поля
return json.decodeFromString<User>(jsonString)
}
// Использование:
fun main() {
val json = """{"id":123,"name":"Анна","email":"anna@example.com","active":true}"""
val user = parseWithKotlinx(json)
println(user.name) // Анна
}
Парсинг с помощью Gson
import com.google.gson.Gson
fun parseWithGson(jsonString: String): User {
val gson = Gson()
return gson.fromJson(jsonString, User::class.java)
}
// Использование:
fun main() {
val json = """{"id":123,"name":"Анна","email":"anna@example.com","active":true}"""
val user = parseWithGson(json)
println(user.email) // anna@example.com
}
Сериализация: превращаем объект в JSON
Обратный процесс — создание JSON-строки из объекта — не менее важен для отправки данных на сервер.
Сериализация с kotlinx.serialization
import kotlinx.serialization.encodeToString
fun serializeWithKotlinx(user: User): String {
val json = Json { prettyPrint = true }
return json.encodeToString(user)
}
// Результат:
// {
// "id": 123,
// "name": "Анна",
// "email": "anna@example.com",
// "active": true
// }
Сериализация с Gson
fun serializeWithGson(user: User): String {
val gson = GsonBuilder().setPrettyPrinting().create()
return gson.toJson(user)
}
Работа со сложными структурами: вложенные объекты и списки
В реальных проектах JSON редко бывает плоским. Научимся работать со списками и вложенными объектами.
Пример: список пользователей с адресами
{
"users": [
{
"id": 1,
"name": "Иван",
"address": {
"city": "Москва",
"street": "Тверская"
}
},
{
"id": 2,
"name": "Мария",
"address": {
"city": "Санкт-Петербург",
"street": "Невский"
}
}
]
}
Data Class для сложной структуры
@Serializable
data class Address(val city: String, val street: String)
@Serializable
data class UserWithAddress(
val id: Int,
val name: String,
val address: Address
)
@Serializable
data class UserList(val users: List<UserWithAddress>)
Парсинг списка
fun parseUserList(jsonString: String): UserList {
val json = Json { ignoreUnknownKeys = true }
return json.decodeFromString<UserList>(jsonString)
}
fun main() {
val json = """
{
"users": [
{"id": 1, "name": "Иван", "address": {"city": "Москва", "street": "Тверская"}},
{"id": 2, "name": "Мария", "address": {"city": "Санкт-Петербург", "street": "Невский"}}
]
}
""".trimIndent()
val result = parseUserList(json)
result.users.forEach { println("${it.name} живет в ${it.address.city}") }
}
Обработка ошибок и null-значений
JSON из внешних источников часто содержит неожиданные данные. Рассмотрим, как сделать код устойчивым.
Null-поля и значения по умолчанию
Если поле может отсутствовать, используйте nullable-тип (String?) или значение по умолчанию.
@Serializable
data class Product(
val id: Int,
val name: