Библиотека Jackson стала стандартом для работы с JSON в Java-экосистеме, а благодаря отличной совместимости с Kotlin она широко применяется и в Android-разработке. В этой статье мы разберём, как с помощью аннотаций Jackson json kotlin управлять сериализацией и десериализацией, настраивать ObjectMapper и избегать подводных камней при работе с data-классами.
Быстрый старт с Jackson в Kotlin-проекте
Чтобы подключить Jackson, добавьте в build.gradle.kts зависимость. Для работы с Kotlin-классами необходимо также подключить модуль поддержки Kotlin, который использует рефлексию времени исполнения.
dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.1")
}
После синхронизации Gradle вы можете сразу создать экземпляр ObjectMapper с поддержкой Kotlin-классов:
val mapper = jacksonObjectMapper() // расширение из модуля Kotlin
Без этого модуля Jackson не сможет корректно инстанцировать Kotlin-объекты, так как у них нет конструкторов по умолчанию в привычном для Java смысле. Модуль решает эту проблему автоматически.
Data-классы и базовая сериализация
Jackson умеет работать с обычными data-классами без каких-либо аннотаций. Например:
data class User(
val id: Int,
val name: String,
val email: String
)
val mapper = jacksonObjectMapper()
val json = mapper.writeValueAsString(User(1, "Анна", "anna@example.com"))
// {"id":1,"name":"Анна","email":"anna@example.com"}
Обратное преобразование — десериализация — также тривиально:
val user = mapper.readValue<User>("""{"id":2,"name":"Иван","email":"ivan@example.com"}""")
Однако в реальных проектах имена полей в JSON часто не совпадают с названиями свойств класса. Именно здесь вступают в игру аннотации.
Аннотация @JsonProperty: переименование полей
Когда сервер отдаёт ключ "user_name", а в классе привычнее видеть userName, аннотация @JsonProperty связывает их.
data class User(
@JsonProperty("user_id") val id: Int,
@JsonProperty("user_name") val name: String,
@JsonProperty("user_email") val email: String
)
Теперь Jackson будет ожидать в JSON ключи "user_id", "user_name" и "user_email", а в Kotlin-коде вы работаете с привычными именами полей. Это особенно полезно при интеграции с API, которые следуют стилю snake_case.
@JsonIgnore: исключение полей из сериализации
Некоторые свойства класса не должны попадать в JSON — например, пароль или внутренний флаг. Аннотация @JsonIgnore полностью исключает поле как из вывода, так и из чтения.
data class Account(
val login: String,
@JsonIgnore val password: String,
val active: Boolean
)
Пароль не будет сериализован в JSON и не будет прочитан из JSON, если он там присутствует. Для более тонкой настройки можно использовать @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) или READ_ONLY.
@JsonInclude: управление null и пустыми значениями
По умолчанию Jackson включает поля со значением null в результирующий JSON. Аннотация @JsonInclude позволяет управлять этим поведением на уровне класса или отдельного поля.
@JsonInclude(JsonInclude.Include.NON_NULL)
data class Profile(
val name: String?,
val age: Int?,
val city: String?
)
Теперь, если поле age равно null, оно вообще не появится в JSON. Это уменьшает объём передаваемых данных и делает ответы чище.
@JsonFormat: форматирование дат и чисел
Даты и временные метки требуют единого формата. Аннотация @JsonFormat задаёт шаблон для сериализации и десериализации.
data class Event(
val title: String,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm")
val date: LocalDateTime
)
В JSON поле date будет записано как строка вроде "05-02-2025 14:30". Без аннотации Jackson сериализует LocalDateTime в массив чисел, что неудобно для чтения человеком.
Собственные сериализаторы и десериализаторы
Если аннотаций недостаточно, Jackson позволяет написать собственный обработчик. Это делается через наследование JsonSerializer или JsonDeserializer и регистрацию в модуле Kotlin.
class RatingSerializer : JsonSerializer<Int>() {
override fun serialize(value: Int, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeString("★".repeat(value.coerceIn(0..5)))
}
}
data class Movie(
val title: String,
@JsonSerialize(using = RatingSerializer::class) val rating: Int
)
Теперь число рейтинга будет представлено звёздочками. При чтении же может потребоваться обратное преобразование, но для такого специфического поля лучше использовать и десериализатор.
Обзор ключевых аннотаций Jackson
| Аннотация | Назначение | Пример |
|---|---|---|
| @JsonProperty | Задаёт имя поля в JSON | @JsonProperty("full_name") val name: String |
| @JsonIgnore | Исключает поле из обработки | @JsonIgnore val password: String |
| @JsonInclude | Управляет включением null/пустых значений | @JsonInclude(NON_NULL) class User(...) |
| @JsonFormat | Задаёт формат даты, чисел и т.д. | @JsonFormat(pattern = "dd.MM.yyyy") val birthDate: LocalDate |
| @JsonSerialize | Указывает кастомный сериализатор | @JsonSerialize(using = MySerializer::class) |
| @JsonDeserialize | Указывает кастомный десериализатор | @JsonDeserialize(using = MyDeserializer::class) |
Практические рекомендации
При работе с jackson json kotlin соблюдайте несколько правил, которые сэкономят время. Во-первых, всегда используйте jacksonObjectMapper() вместо обычного ObjectMapper() — это включает поддержку Kotlin и корректную работу с data-классами. Во-вторых, тестируйте сериализацию и десериализацию на разных входных данных, особенно если сервер может прислать неполный JSON: добавьте значения по умолчанию в data-класс. В-третьих, при обфускации приложения настраивайте правила ProGuard/R8, чтобы аннотации Jackson сохранились — иначе ваш парсинг сломается на релизной сборке.
Коротко об изученном
Вы освоили аннотации Jackson для Kotlin и теперь можете гибко управлять сериализацией JSON. Библиотека jackson json kotlin даёт огромные возможности: от простого переименования полей до кастомных сериализаторов и тонкого контроля формата дат. Используйте эти знания в своих проектах — и работа с JSON станет предсказуемой и удобной.