Работа с JSON в Kotlin: полное руководство по парсингу и сериализации
JSON (JavaScript Object Notation) остается одним из самых популярных форматов обмена данными в современной веб-разработке. При работе с Kotlin, будь то серверная часть на Ktor или мобильное приложение под Android, умение эффективно парсить и сериализовать JSON — обязательный навык. В этой статье мы разберем три основных подхода к работе с JSON в Kotlin: использование библиотек Gson, Moshi и официального решения от JetBrains — kotlinx.serialization.
Почему Kotlin требует особого подхода к JSON?
Kotlin — статически типизированный язык с такими особенностями, как null-безопасность, data-классы и свойства по умолчанию. Библиотеки для работы с JSON должны учитывать эти особенности, чтобы код был не только рабочим, но и идиоматичным. Неправильная настройка может привести к NullPointerException или неожиданному поведению при десериализации.
1. Использование Gson для работы с JSON
Gson от Google — одна из самых старых и распространенных библиотек. Она проста в использовании, но имеет некоторые особенности при работе с Kotlin.
Подключение зависимости
// build.gradle.kts
implementation("com.google.code.gson:gson:2.10.1")
Сериализация объекта в JSON
import com.google.gson.Gson
data class User(
val id: Int,
val name: String,
val email: String? = null
)
fun main() {
val user = User(1, "Иван Петров", "ivan@example.com")
val gson = Gson()
val json = gson.toJson(user)
println(json)
// Вывод: {"id":1,"name":"Иван Петров","email":"ivan@example.com"}
}
Парсинг JSON в объект
fun parseUser(jsonString: String): User {
val gson = Gson()
return gson.fromJson(jsonString, User::class.java)
}
fun main() {
val json = """{"id":2,"name":"Анна Смирнова","email":null}"""
val user = parseUser(json)
println(user.name) // Анна Смирнова
}
Важно: Gson использует рефлексию Java и может не учитывать null-безопасность Kotlin. При десериализации поля, объявленные как non-null, могут получить значение null, что приведет к ошибке во время выполнения. Рекомендуется использовать @JvmOverloads и конструкторы со значениями по умолчанию.
2. Moshi — современная альтернатива от Square
Moshi создавался с учетом особенностей Kotlin и предлагает лучшую интеграцию с null-безопасностью и data-классами.
Подключение зависимости
// build.gradle.kts
implementation("com.squareup.moshi:moshi-kotlin:1.15.0")
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.15.0")
Пример работы с Moshi
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
data class Product(
val id: Int,
val title: String,
val price: Double,
val inStock: Boolean = false
)
fun main() {
val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(Product::class.java)
// Сериализация
val product = Product(101, "Ноутбук", 89999.99, true)
val json = adapter.toJson(product)
println(json)
// {"id":101,"title":"Ноутбук","price":89999.99,"inStock":true}
// Десериализация
val jsonString = """{"id":102,"title":"Мышь","price":2500.00}"""
val parsedProduct = adapter.fromJson(jsonString)
println(parsedProduct?.title) // Мышь
println(parsedProduct?.inStock) // false (значение по умолчанию)
}
Moshi автоматически обрабатывает значения по умолчанию в data-классах и корректно работает с nullable-типами. Для лучшей производительности рекомендуется использовать кодогенерацию через аннотацию @JsonClass.
3. kotlinx.serialization — официальное решение от JetBrains
Это наиболее "родной" способ работы с JSON в Kotlin. Библиотека использует компиляторные плагины и не полагается на рефлексию, что делает ее быстрой и безопасной.
Подключение зависимости
// build.gradle.kts
plugins {
kotlin("plugin.serialization") version "1.9.0"
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}
Пример с аннотацией @Serializable
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
@Serializable
data class Book(
val isbn: String,
val title: String,
val author: String,
val year: Int? = null
)
fun main() {
val json = Json {
prettyPrint = true
ignoreUnknownKeys = true // игнорировать неизвестные поля
}
// Сериализация
val book = Book("978-5-17-123456-7", "Kotlin в действии", "Дмитрий Жемеров", 2023)
val jsonString = json.encodeToString(book)
println(jsonString)
// Десериализация
val input = """{"isbn":"978-5-17-987654-3","title":"Программирование на Kotlin","author":"Андрей Бреслав"}"""
val parsedBook = json.decodeFromString<Book>(input)
println(parsedBook.title) // Программирование на Kotlin
println(parsedBook.year) // null
}
Работа с вложенными объектами и списками
@Serializable
data class Author(val name: String, val age: Int)
@Serializable
data class Library(
val name: String,
val books: List<Book>,
val authors: Map<String, Author>
)
fun main() {
val library = Library(
name = "Центральная библиотека",
books = listOf(
Book("1", "Kotlin Basics", "John Doe"),
Book("2", "Advanced Kotlin", "Jane Smith")
),
authors = mapOf(
"john" to Author("John Doe", 35),
"jane" to Author("Jane Smith", 28)
)
)
val json = Json { prettyPrint = true }
println(json.encodeToString(library))
}