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, стойности по подразбиране и 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 срещу Gson

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

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

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

ХарактеристикаGsonMoshi
Механизъмрефлексиягенериране на код / рефлексия
Null safetyне взема предвидпълна поддръжка на Kotlin
Скоростсреднависока
Стойности по подразбиранене поддържаподдържа
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. За модели със стойности по подразбиране и nullable полета Moshi се държи по-предвидимо: ако non-null поле без стойност по подразбиране липсва в JSON, Moshi хвърля JsonDataException, предотвратявайки скрити NPE. Интеграцията с Retrofit чрез MoshiConverterFactory се добавя с една зависимост и не изисква промяна на архитектурата на мрежовия слой. За объркване чрез ProGuard или R8 трябва да се добавят правила за запазване на класове, анотирани с @JsonClass, и генерирани адаптери, в противен случай сериализацията ще се счупи в release версията. Като цяло, миграцията от Gson към Moshi е оправдана в нови Kotlin проекти, където производителността и типовата безопасност са важни.

kotlin
// Сравнение на сериализация: Gson срещу 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 Kb). Moshi също поддържа Kotlin Multiplatform и стойности по подразбиране в 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 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също