Moshi este o bibliotecă JSON modernă de la Square, creată special pentru Kotlin și Android ținând cont de limitările Gson. Este complet compatibilă cu null-safety din Kotlin, generează cod în faza de compilare și nu utilizează reflexie, ceea ce crește performanța și fiabilitatea. Conform datelor Square Moshi, 2024, Moshi asigură serializare predictibilă și suportă adaptoare personalizate pentru orice tipuri de date.
Principalele puncte
Moshi este o bibliotecă JSON pentru JVM, Android și Kotlin Multiplatform, creată de Square (autorii OkHttp și Retrofit). Spre deosebire de Gson, Moshi nu se bazează pe reflexie — adaptoarele sunt generate în faza de compilare prin adnotarea @JsonClass(generateAdapter = true). Acest lucru face Moshi mai rapid, mai sigur și mai predictibil în lucrul cu construcții specifice Kotlin.
Principala diferență a Moshi față de predecesori este renunțarea la reflexie. Reflexia permite Gson să lucreze cu orice clasă fără pregătire, dar prețul este inițializarea lentă, imposibilitatea optimizării de către compilator și riscul de erori în timpul execuției. Moshi necesită indicarea explicită a claselor pentru generarea de cod, dar în schimb oferă viteza codului scris manual și siguranța completă a tipurilor în faza de compilare.
// Conectarea Moshi în 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"
}
// Model simplu cu generare de cod
@JsonClass(generateAdapter = true)
data class User(
@Json(name = "user_id")
val id: Int,
val name: String,
val email: String,
val avatar: String? = null
)
// Utilizare
val moshi = Moshi.Builder()
.build()
val jsonAdapter = moshi.adapter(User::class.java)
Pentru a începe lucrul cu Moshi, trebuie adăugate dependințele în build.gradle și adnotate modelele. Moshi.Builder servește ca punct de intrare: prin el se adaugă adaptoare încorporate pentru tipuri standard, adaptoare personalizate și se configurează comportamentul bibliotecii. Moshi suportă adaptoare pentru Date, Enum, Collection și Map din cutie, dar pentru clasele Kotlin este necesar modulul moshi-kotlin. Spre deosebire de Gson, Moshi nu utilizează reflexia pentru clasele Kotlin implicit — pentru aceasta se conectează KotlinJsonAdapterFactory, care servește ca opțiune de rezervă când generarea de cod nu este aplicată sau clasa nu este adnotată cu @JsonClass. Această abordare garantează că dezvoltatorul alege explicit între performanța generării de cod și flexibilitatea reflexiei pentru fiecare clasă în parte.
După construirea Moshi prin Builder, dezvoltatorul obține o instanță Moshi și solicită un adaptor pentru clasa dorită. JsonAdapter este obiectul central care execută serializarea prin toJson() și deserializarea prin fromJson(). Moshi utilizează automat adaptorul generat dacă clasa este adnotată cu @JsonClass(generateAdapter = true), altfel aplică KotlinJsonAdapterFactory reflexiv ca opțiune de rezervă. Această abordare combină viteza generării de cod cu flexibilitatea mecanismului reflexiv pentru proiecte de orice scară și nivel de complexitate. Moshi este potrivit atât pentru aplicații mici, cât și pentru proiecte corporative mari cu sute de modele de date.
// Configurarea Moshi cu KotlinJsonAdapterFactory
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.add(LocalDateAdapter())
.build()
// Utilizarea adaptorului
val adapter = moshi.adapter(User::class.java)
// Serializare
val user = User(1, "Alice", "alice@test.com")
val json = adapter.toJson(user)
// Deserializare
val jsonString = """{"user_id":2,"name":"Bob","email":"bob@test.com"}"""
val parsedUser = adapter.fromJson(jsonString)
// Lucrul cu lista
val listAdapter = moshi.adapter(
Types.newParameterizedType(
List::class.java,
User::class.java
)
)
Moshi utilizează adnotări pentru configurarea serializării și suportul tipurilor personalizate. @Json(name = "...") setează cheia JSON pentru câmp. @Transient exclude câmpul din serializare. @JsonClass(generateAdapter = true) activează generarea de cod. Pentru logică personalizată, Moshi oferă adnotările @ToJson și @FromJson, care pot fi plasate într-o clasă de adaptor separată.
Adnotarea @Json înlocuiește @SerializedName din Gson și funcționează similar: câmpul kotlinName este legat de cheia JSON „kotlin_name". Pentru tipurile pe care Moshi nu le poate serializa implicit (de exemplu, LocalDate), dezvoltatorul creează o clasă cu metode @ToJson și @FromJson. Adaptoarele se înregistrează prin Moshi.Builder.add() și se aplică global sau pentru un tip specific. Moshi suportă sealed class și serializarea polimorfă prin @JsonClass cu indicarea explicită a discriminatorului, ceea ce permite lucrul cu ierarhii de tipuri în JSON fără verificarea manuală a câmpurilor. La deserializare, Moshi ignoră implicit cheile necunoscute în JSON, ceea ce asigură compatibilitatea inversă la adăugarea de noi câmpuri pe partea serverului fără modificarea codului client. Pentru depanare se poate activa modul strict prin failOnUnknown, care aruncă o excepție la detectarea cheilor necunoscute.
// Adaptor personalizat pentru 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)
}
}
// Model cu adnotări 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
)
// Înregistrarea adaptorului
val moshi = Moshi.Builder()
.add(LocalDateAdapter())
.add(KotlinJsonAdapterFactory())
.build()
Compararea Moshi și Gson este o întrebare frecventă la alegerea unei biblioteci JSON pentru un proiect Android. Moshi câștigă în dezvoltarea modernă Kotlin datorită generării de cod, null-safety și vitezei. Gson rămâne relevant pentru proiecte Java, cod moștenit și scenarii unde configurația minimă este importantă. Diferența devine vizibilă la volume mari de date și modele complexe.
Testele de performanță arată că Moshi cu generare de cod funcționează de 2-5 ori mai rapid decât Gson la operațiile de serializare și deserializare. Avantajul cheie al Moshi este gestionarea corectă a null-safety din Kotlin: dacă un câmp lipsește în JSON, iar în model este declarat ca non-null fără valoare implicită, Moshi aruncă o excepție în faza de deserializare, prevenind erorile ascunse.
| Caracteristică | Gson | Moshi |
|---|---|---|
| Mecanism | reflexie | generare de cod / reflexie |
| Null safety | nu ia în considerare | suport complet Kotlin |
| Viteză | medie | mare |
| Valori implicite | nu suportă | suportă |
| Kotlin Multiplatform | nu | da |
| Dimensiune bibliotecă | ~240 Kb | ~150 Kb |
Alegerea între Moshi și Gson depinde de contextul proiectului. Proiectele noi pe Kotlin beneficiază de Moshi datorită siguranței tipurilor și performanței. Gson rămâne o alegere rezonabilă pentru suportul codului Java, structuri JSON dinamice sau când simplitatea conectării este mai importantă decât viteza. Pentru Kotlin Multiplatform, Moshi este singurul dintre cele două variante care suportă această platformă.
La migrarea de la Gson la Moshi, modificările principale privesc adnotările și adaptoarele. @SerializedName din Gson se înlocuiește cu @Json(name = "..."), iar JsonSerializer/JsonDeserializer personalizate — cu perechea @ToJson/@FromJson. Pentru modelele cu valori implicite și câmpuri nullable, Moshi se comportă mai predictibil: dacă în JSON lipsește un câmp non-null fără valoare implicită, Moshi aruncă JsonDataException, prevenind NPE ascunse. Integrarea cu Retrofit prin MoshiConverterFactory se adaugă cu o singură dependență și nu necesită modificarea arhitecturii stratului de rețea. Pentru ofuscarea prin ProGuard sau R8 trebuie adăugate reguli de păstrare a claselor adnotate cu @JsonClass și a adaptoarelor generate, altfel serializarea se va strica în versiunea release. În general, migrarea de la Gson la Moshi este justificată în proiecte noi Kotlin unde performanța și siguranța tipurilor sunt importante.
// Compararea serializării: Gson vs Moshi
data class Sample(
val name: String,
val count: Int,
val tags: List<String> = listOf()
)
// Gson: funcționează prin reflexie
val gson = Gson()
val fromGson = gson.fromJson("""{"name":"test"}""",
Sample::class.java)
// count = 0 (default), dar null-safety nu este verificat
// Moshi: necesită adaptor, null-safety este explicit
@JsonClass(generateAdapter = true)
data class SampleMoshi(
val name: String,
val count: Int,
val tags: List<String> = listOf()
)
Întrebări frecvente
Moshi este o bibliotecă JSON de la Square pentru Kotlin și Android, care utilizează generarea de cod în locul reflexiei. Asigură performanță ridicată, gestionarea corectă a null-safety din Kotlin și compatibilitate cu Kotlin Multiplatform.
Moshi întrece Gson în viteză (de 2-5 ori mai rapid datorită generării de cod), siguranță (ia în considerare adnotările null din Kotlin) și dimensiune (mai mic cu ~90 Kb). Moshi suportă de asemenea Kotlin Multiplatform și valori implicite în data class.
@JsonClass(generateAdapter = true) indică Moshi să genereze un adaptor pentru această clasă în faza de compilare. Adaptorul generat execută serializarea direct, fără reflexie, oferind performanță maximă.
Creați o clasă cu metode adnotate cu @ToJson (serializare) și @FromJson (deserializare). Înregistrați instanța prin Moshi.Builder.add(). Moshi va găsi și aplica automat adaptorul la lucrul cu tipul corespunzător.
Da, Moshi suportă Kotlin Multiplatform începând cu versiunea 1.13.0. Acest lucru îl face singura soluție JSON populară pentru proiecte KMP, permițând utilizarea codului comun de serializare pe toate platformele țintă.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și