Moshi: ключевые понятия, JSON-библиотека Kotlin и как работает

Автор: IT Sectr Опубликовано: 2026-03-15 Время чтения: 8 мин

Moshi — современная JSON-библиотека от Square, созданная специально для Kotlin и Android с учётом ограничений Gson. Она полностью совместима с null-безопасностью Kotlin, генерирует код на этапе компиляции и не использует рефлексию, что повышает производительность и надёжность. По данным Square Moshi, 2024, Moshi обеспечивает предсказуемую сериализацию и поддерживает кастомные адаптеры для любых типов данных.

Главное

  • Moshi — JSON-библиотека от Square для Kotlin и Android без рефлексии
  • Kotlin-адаптер — встроенная поддержка data class, default values и null safety
  • @Json — аннотация для настройки имени поля и игнорирования свойств
  • Адаптеры — кастомная логика сериализации через @ToJson и @FromJson
  • Кодогенерация — Moshi генерирует адаптеры на этапе компиляции через kapt или KSP

Что такое Moshi

Moshi — это JSON-библиотека для JVM, Android и Kotlin Multiplatform, созданная Square (авторами OkHttp и Retrofit). В отличие от Gson, Moshi не полагается на рефлексию — адаптеры генерируются на этапе компиляции через аннотацию @JsonClass(generateAdapter = true). Это делает Moshi быстрее, безопаснее и предсказуемее в работе с Kotlin-специфичными конструкциями.

Философия и преимущества

Основное отличие Moshi от предшественников — отказ от рефлексии. Рефлексия позволяет Gson работать с любым классом без подготовки, но платой служат медленная инициализация, невозможность оптимизации компилятором и риск ошибок во время выполнения. Moshi требует явного указания классов для кодогенерации, но взамен даёт скорость рукописного кода и полную типобезопасность на этапе компиляции.

kotlin
// Подключение Moshi в build.gradle
dependencies {
    implementation "com.squareup.moshi:moshi:1.15.0"
    implementation "com.squareup.moshi:moshi-kotlin:1.15.0"
    kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.0"
}

// Простая модель с кодогенерацией
@JsonClass(generateAdapter = true)
data class User(
    @Json(name = "user_id")
    val id: Int,
    val name: String,
    val email: String,
    val avatar: String? = null
)

// Использование
val moshi = Moshi.Builder()
    .build()
val jsonAdapter = moshi.adapter(User::class.java)

Подключение и настройка

Для начала работы с Moshi требуется добавить зависимости в build.gradle и аннотировать модели. Moshi.Builder служит точкой входа: через него добавляются встроенные адаптеры для стандартных типов, кастомные адаптеры и настраивается поведение библиотеки. Moshi поддерживает адаптеры для Date, Enum, Collection и Map из коробки, но для Kotlin-классов требуется moshi-kotlin-модуль. В отличие от Gson, Moshi не использует рефлексию для Kotlin-классов по умолчанию — для этого подключается KotlinJsonAdapterFactory, который служит запасным вариантом, когда кодогенерация не применяется или класс не аннотирован @JsonClass. Такой подход гарантирует, что разработчик явно выбирает между производительностью кодогенерации и гибкостью рефлексии для каждого конкретного класса.

Создание Moshi и добавление адаптеров

После сборки Moshi через Builder разработчик получает экземпляр Moshi и запрашивает адаптер для нужного класса. JsonAdapter — центральный объект, который выполняет сериализацию через toJson() и десериализацию через fromJson(). Moshi автоматически использует сгенерированный адаптер, если класс аннотирован @JsonClass(generateAdapter = true), иначе применяет рефлексивный KotlinJsonAdapterFactory как запасной вариант. Такой подход сочетает скорость кодогенерации с гибкостью рефлексивного механизма для проектов любого масштаба и уровня сложности. Moshi отлично подходит как для небольших приложений, так и для крупных корпоративных проектов с сотнями моделей данных.

kotlin
// Настройка Moshi с KotlinJsonAdapterFactory
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .add(LocalDateAdapter())
    .build()

// Использование адаптера
val adapter = moshi.adapter(User::class.java)

// Сериализация
val user = User(1, "Alice", "alice@test.com")
val json = adapter.toJson(user)

// Десериализация
val jsonString = """{"user_id":2,"name":"Bob","email":"bob@test.com"}"""
val parsedUser = adapter.fromJson(jsonString)

// Работа со списком
val listAdapter = moshi.adapter(
    Types.newParameterizedType(
        List::class.java,
        User::class.java
    )
)

Аннотации и адаптеры

Moshi использует аннотации для настройки сериализации и поддержки кастомных типов. @Json(name = "...") задаёт JSON-ключ для поля. @Transient исключает поле из сериализации. @JsonClass(generateAdapter = true) включает кодогенерацию. Для кастомной логики Moshi предоставляет аннотации @ToJson и @FromJson, которые можно разместить в отдельном классе-адаптере.

@Json и кастомные адаптеры

Аннотация @Json заменяет Gson-овский @SerializedName и работает аналогично: поле kotlinName связывается с JSON-ключом "kotlin_name". Для типов, которые Moshi не умеет сериализовать по умолчанию (например, LocalDate), разработчик создаёт класс с методами @ToJson и @FromJson. Адаптеры регистрируются через Moshi.Builder.add() и применяются глобально или к конкретному типу. Moshi поддерживает sealed class и полиморфную сериализацию через @JsonClass с явным указанием дискриминатора, что позволяет работать с иерархиями типов в JSON, не прибегая к ручной проверке полей. При десериализации Moshi по умолчанию игнорирует неизвестные ключи в JSON, что обеспечивает обратную совместимость при добавлении новых полей на стороне сервера без изменения клиентского кода. Для отладки можно включить строгий режим через failOnUnknown, который выбрасывает исключение при обнаружении неизвестных ключей.

kotlin
// Кастомный адаптер для LocalDate
class LocalDateAdapter {

    @ToJson
    fun toJson(date: LocalDate): String {
        return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
    }

    @FromJson
    fun fromJson(dateString: String): LocalDate {
        return LocalDate.parse(dateString)
    }
}

// Модель с аннотациями Moshi
@JsonClass(generateAdapter = true)
data class Event(
    @Json(name = "event_id")
    val id: Int,

    @Json(name = "event_date")
    val date: LocalDate,

    @Transient
    val localCache: String? = null
)

// Регистрация адаптера
val moshi = Moshi.Builder()
    .add(LocalDateAdapter())
    .add(KotlinJsonAdapterFactory())
    .build()

Moshi vs Gson

Сравнение Moshi и Gson — частый вопрос при выборе JSON-библиотеки для Android-проекта. Moshi выигрывает в современной Kotlin-разработке благодаря кодогенерации, null-безопасности и скорости. Gson остаётся актуальным для Java-проектов, legacy-кода и сценариев, где важна минимальная конфигурация. Разница становится заметной на больших объёмах данных и сложных моделях.

Производительность и безопасность

Тесты производительности показывают, что Moshi с кодогенерацией работает в 2-5 раз быстрее Gson на операциях сериализации и десериализации. Ключевое преимущество Moshi — корректная обработка null-безопасности Kotlin: если в JSON поле отсутствует, а в модели оно объявлено как non-null без значения по умолчанию, Moshi выбрасывает исключение на этапе десериализации, предотвращая скрытые ошибки.

ХарактеристикаGsonMoshi
Механизмрефлексиякодогенерация / рефлексия
Null safetyне учитываетполная поддержка Kotlin
Скоростьсредняявысокая
Default valuesне поддерживаетподдерживает
Kotlin Multiplatformнетда
Размер библиотеки~240 Kb~150 Kb

Выбор между Moshi и Gson зависит от контекста проекта. Новые проекты на Kotlin выигрывают от Moshi благодаря типобезопасности и производительности. Gson остаётся разумным выбором для поддержки Java-кода, динамических структур JSON или когда простота подключения важнее скорости. Для Kotlin Multiplatform Moshi является единственным из двух вариантов, поддерживающим эту платформу.

При миграции с Gson на Moshi основные изменения касаются аннотаций и адаптеров. Gson-овские @SerializedName заменяются на @Json(name = "..."), а кастомные JsonSerializer/JsonDeserializer — на пару @ToJson/@FromJson. Для моделей с default values и nullable-полями Moshi ведёт себя более предсказуемо: если в JSON отсутствует non-null поле без умолчания, Moshi выбрасывает JsonDataException, предотвращая скрытые NPE. Интеграция с Retrofit через MoshiConverterFactory добавляется одной зависимостью и не требует изменения архитектуры сетевого слоя. Для обфускации через ProGuard или R8 необходимо добавить правила сохранения @JsonClass-аннотированных классов и сгенерированных адаптеров, иначе сериализация сломается в релизной сборке. В целом миграция с Gson на Moshi оправдана в новых Kotlin-проектах, где важны производительность и типобезопасность.

kotlin
// Сравнение сериализации: Gson vs Moshi
data class Sample(
    val name: String,
    val count: Int,
    val tags: List<String> = listOf()
)

// Gson: работает через рефлексию
val gson = Gson()
val fromGson = gson.fromJson("""{"name":"test"}""",
    Sample::class.java)
// count = 0 (default), но null-небезопасность не проверяется

// Moshi: требует адаптера, null-безопасность явная
@JsonClass(generateAdapter = true)
data class SampleMoshi(
    val name: String,
    val count: Int,
    val tags: List<String> = listOf()
)

Часто задаваемые вопросы

Что такое Moshi в Android?

Moshi — это JSON-библиотека от Square для Kotlin и Android, использующая кодогенерацию вместо рефлексии. Она обеспечивает высокую производительность, корректную обработку null-безопасности Kotlin и совместимость с Kotlin Multiplatform.

Чем Moshi лучше Gson?

Moshi превосходит Gson по скорости (в 2-5 раз быстрее благодаря кодогенерации), безопасности (учитывает null-аннотации Kotlin) и размеру (меньше на ~90 Кб). Moshi также поддерживает Kotlin Multiplatform и default values в data class.

Как работает аннотация @JsonClass в Moshi?

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

Как создать кастомный адаптер Moshi?

Создайте класс с методами, аннотированными @ToJson (сериализация) и @FromJson (десериализация). Зарегистрируйте экземпляр через Moshi.Builder.add(). Moshi автоматически найдёт и применит адаптер при работе с соответствующим типом.

Поддерживает ли Moshi Kotlin Multiplatform?

Да, Moshi поддерживает Kotlin Multiplatform начиная с версии 1.13.0. Это делает его единственным популярным JSON-решением для KMP-проектов, позволяя использовать общий код сериализации на всех целевых платформах.

Итоги

  • Moshi — современная JSON-библиотека от Square с кодогенерацией вместо рефлексии
  • @JsonClass — аннотация для генерации адаптера, обеспечивающая скорость рукописного кода
  • @Json — настройка JSON-ключей, @Transient — исключение полей из сериализации
  • @ToJson и @FromJson — простой API для кастомных адаптеров любых типов
  • Null safety — Moshi учитывает Kotlin-аннотации и выбрасывает исключение при несоответствии
  • Производительность — в 2-5 раз быстрее Gson на операциях сериализации и десериализации
  • Kotlin Multiplatform — поддержка KMP для универсального кода сериализации

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также