Moshi је модерна JSON библиотека компаније Square, креирана посебно за Kotlin и Android узимајући у обзир ограничења Gson-а. Потпуно је компатибилна са null-безбедношћу Kotlin-а, генерише код у фази компилације и не користи рефлексију, што повећава перформансе и поузданост. Према подацима Square Moshi, 2024, Moshi обезбеђује предвидљиву серијализацију и подржава прилагођене адаптере за све типове података.
Главно
Moshi је JSON библиотека за JVM, Android и Kotlin Multiplatform, коју је креирао Square (аутори OkHttp и Retrofit). За разлику од Gson-а, Moshi се не ослања на рефлексију — адаптери се генеришу у фази компилације путем анотације @JsonClass(generateAdapter = true). То чини Moshi бржим, безбеднијим и предвидљивијим у раду са Kotlin-специфичним конструкцијама.
Основна разлика Moshi-ја од претходника је одбацивање рефлексије. Рефлексија омогућава Gson-у да ради са било којом класом без припреме, али цена су спора иницијализација, немогућност оптимизације од стране компилатора и ризик од грешака током извршавања. Moshi захтева експлицитно навођење класа за кодогенерацију, али заузврат даје брзину ручно писаног кода и потпуну типску безбедност у фази компилације.
// Повезивање 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 служи као улазнa тачка: кроз њега се додају уграђени адаптери за стандардне типове, прилагођени адаптери и подешава понашање библиотеке. Moshi подржава адаптере за Date, Enum, Collection и Map из кутије, али за Kotlin класе је потребан moshi-kotlin модул. За разлику од Gson-а, Moshi не користи рефлексију за Kotlin класе подразумевано — за то се повезује KotlinJsonAdapterFactory, који служи као резервна опција када се кодогенерација не примењује или класа није анотирана са @JsonClass. Такав приступ гарантује да програмер експлицитно бира између перформанси кодогенерације и флексибилности рефлексије за сваку конкретну класу.
Након изградње Moshi-ја путем Builder-а, програмер добија инстанцу Moshi-ја и захтева адаптер за потребну класу. JsonAdapter је централни објекат који обавља серијализацију путем toJson() и десеријализацију путем fromJson(). Moshi аутоматски користи генерисани адаптер ако је класа анотирана са @JsonClass(generateAdapter = true), иначе примењује рефлексивни KotlinJsonAdapterFactory као резервну опцију. Овај приступ комбинује брзину кодогенерације са флексибилношћу рефлексивног механизма за пројекте било које величине и нивоа сложености. Moshi је одличан како за мале апликације, тако и за велике корпоративне пројекте са стотинама модела података.
// Подешавање 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 замењује Gson-ов @SerializedName и ради слично: поље kotlinName се повезује са JSON кључем „kotlin_name". За типове које Moshi не уме да серијализује подразумевано (нпр. LocalDate), програмер креира класу са методама @ToJson и @FromJson. Адаптери се региструју путем Moshi.Builder.add() и примењују се глобално или на конкретан тип. Moshi подржава sealed class и полиморфну серијализацију путем @JsonClass са експлицитним навођењем дискриминатора, што омогућава рад са хијерархијама типова у JSON-у без ручне провере поља. При десеријализацији Moshi подразумевано игнорише непознате кључеве у JSON-у, што обезбеђује уназадну компатибилност при додавању нових поља на страни сервера без промене клијентског кода. За отклањање грешака може се укључити строги режим путем failOnUnknown, који избацује изузетак при откривању непознатих кључева.
// Прилагођени адаптер за 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-а је често питање при избору JSON библиотеке за Android пројекат. Moshi побеђује у савременом Kotlin развоју захваљујући кодогенерацији, null-безбедности и брзини. Gson остаје актуелан за Java пројекте, заоставштински код и сценарије где је минимална конфигурација важна. Разлика постаје приметна на великим количинама података и сложеним моделима.
Тестови перформанси показују да Moshi са кодогенерацијом ради 2-5 пута брже од Gson-а на операцијама серијализације и десеријализације. Кључна предност Moshi-ја је коректно руковање null-безбедношћу Kotlin-а: ако у JSON-у недостаје поље, а у моделу је декларисано као non-null без подразумеване вредности, Moshi избацује изузетак у фази десеријализације, спречавајући скривене грешке.
| Карактеристика | Gson | Moshi |
|---|---|---|
| Механизам | рефлексија | кодогенерација / рефлексија |
| 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 се понаша предвидљивије: ако у JSON-у недостаје non-null поље без подразумеване вредности, Moshi избацује JsonDataException, спречавајући скривене NPE. Интеграција са Retrofit-ом путем MoshiConverterFactory додаје се једном зависношћу и не захтева промену архитектуре мрежног слоја. За обфускацију путем ProGuard-а или R8 потребно је додати правила очувања @JsonClass-анотираних класа и генерисаних адаптера, иначе ће серијализација бити покварена у релизној верзији. У целини, миграција са Gson-а на Moshi је оправдана у новим 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 је JSON библиотека компаније Square за Kotlin и Android која користи кодогенерацију уместо рефлексије. Обезбеђује високе перформансе, коректно руковање null-безбедношћу Kotlin-а и компатибилност са Kotlin Multiplatform.
Moshi надмашује Gson по брзини (2-5 пута бржи захваљујући кодогенерацији), безбедности (узима у обзир null-анотације Kotlin-а) и величини (мањи за ~90 Kb). Moshi такође подржава Kotlin Multiplatform и подразумеване вредности у data class.
@JsonClass(generateAdapter = true) налаже Moshi-ју да генерише адаптер за ову класу у фази компилације. Генерисани адаптер обавља серијализацију директно, без рефлексије, што даје максималне перформансе.
Креирајте класу са методама анотираним са @ToJson (серијализација) и @FromJson (десеријализација). Региструјте инстанцу путем Moshi.Builder.add(). Moshi ће аутоматски пронаћи и применити адаптер при раду са одговарајућим типом.
Да, Moshi подржава Kotlin Multiplatform од верзије 1.13.0. То га чини јединим популарним JSON решењем за KMP пројекте, омогућавајући коришћење заједничког кода серијализације на свим циљним платформама.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође