Moshi: Modernizarea analizei JSON pe Android

Pentru dezvoltarea oricărei aplicații este necesară utilizarea JSON pentru a comunica cu backend și, până acum, lucrul obișnuit a fost să se ajute de la GONS bibliotecă. Deși acest instrument funcționează corect în Java, nu este cea mai bună opțiune pentru Kotlin, deoarece pot apărea erori necontrolate. În ultima vreme au fost dezvoltate alte instrumente care permit același lucru, dar ele sunt mai sigure înainte de acest tip de eșec. Vorbim despre Jackson, Kotlinx.Serializare și Moshi. Deși aceste trei opțiuni prezintă aceleași avantaje față de GSON, ne vom concentra pe Moshi pentru că sunt cele mai populare și cele mai stabile acum.

Ce este Moshi?

Moshi este o librărie la Pauză Json pe obiectele Java sau Kotlin. Este dezvoltat de Piața pentru aproape aceiași oameni pe care Gon. Principalii dezvoltatori, Jesse Wilson și Jake Wharton, au spus că Moshi ar putea fi considerat GON V3.

Principalele avantaje față de GSON sunt:

  • Moshi înțelege și funcționează corect cu NULL Tipuri de Kotlin.
  • Nu mai este necesar să se indice pentru a afișa că nu oferă pachetul în care aveți locurile dvs., deoarece cu configurația de bază este suficientă.
  • > Moshi în dezvoltare și îmbunătățire, în timp ce GSON este practic o librărie finalizată.
  • ocupă mai puțină dimensiune în apk.

** (Jesse Wilson expune mai multe avantaje aici).

Dacă sunteți obișnuiți să utilizați GSON, nu veți avea probleme cu Moshi, deoarece arată foarte mult. Cu toate acestea, este necesar să se ia în considerare unele diferențe și să ia anumite măsuri de precauție atunci când efectuați o migrare.

Utilizarea bibliotecii

Se recomandă utilizarea Moshi împreună Codegen:

implementation "com.squareup.moshi:moshi:$moshi_version"kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"

și nu uitați niciodată Adăugați regulile Proguned: https://github.com/square/moshi/blob/master/moshi/src/main/resources/ME. ..

Pentru ao folosi împreună cu Retrofit Utilizați acest convertor: https://github.com/square/retrofit/tree/master/retrofit-converters/moshi

și adăugați la instanța dvs. de modernizare după cum urmează:

Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(MoshiConverterFactory.create()) .build()

Cu aceasta aveți deja proiectul dvs. gata de utilizare Moshi pentru a opri JSON.

Diferențe cu GSON: modele

unde am avut cu GSON:

data class FooDTO( val id: Int, @SerializedName("nombre") val name: String)

cu Moshi Avem:

@JsonClass(generateAdapter = true)data class FooDTO( val id: Int, @Json(name = "nombre") val name: String)

adnotare @SerializedName Modificări ale @Json și pentru că folosim codgen, este necesar Notați toate clasele cu @JsonClass(generateAdapter = true.

di Feer cu GSON: Deserializator / Serializer

cu GSON ar fi pus în acest fel:

class DateAdapter : JsonDeserializer<Date>, JsonSerializer<Date> { private val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) override fun serialize( src: Date, typeOfSrc: Type, context: JsonSerializationContext ): JsonElement { return JsonPrimitive(df.format(src)) } override fun deserialize( json: JsonElement, typeOfT: Type, context: JsonDeserializationContext ): Date { return df.parse(json.asString)!! }}

și cu Moshi Aceasta:

class DateAdapter { private val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) @ToJson fun toJson(value: Date): String { return df.format(value) } @FromJson fun fromJson(source: String): Date { return df.parse(source)!! }}

diferențe cu gson: kotlin null siguranță

gson nu înțelege tipurile non-null de Kotlin. Aceasta înseamnă că, dacă încercăm să analizăm o valoare nulă într-o rată non-nulibilă, GSON va face fără erori, ceea ce poate provoca excepții neașteptate cu ușurință. De exemplu:

Având în vedere modelul anterior al alimentelor, și un JSON {„ID”: 0, „Nume”: null}:

  • gson va crea obiectul alimentar ( 0, null), deși acest lucru nu ar trebui să fie posibil, deoarece numele a fost definit ca non-nulibil. Nu va lansa nici o excepție, dar dacă veți obține erori atunci când utilizați numele:
    • nume.dempty () va produce o nullpointerexception
    • name.trim () va lansa un tip Cascastexception: null nu poate fi Cutând la tipul non-nul kotlin.hosquequeny
  • moshi va lansa următoarea excepție atunci când deserializează:
    com.squareup.moshi.JsonDataException: Required value 'name' (JSON name 'nombre') missing

cu Moshi Vom ști din primul moment care nu reușește în definiția modelului nostru, cu toate acestea cu GSON nu vom fi conștienți până la momentul utilizării variabilei și pot apărea oriunde în cod și Fiți capabili să aruncați mai multe tipuri de eroare în funcție de utilizarea variabilei și toate acestea se vor întâmpla în timp ce vă întrebați cum a fost posibil ca o variabilă non-nulă să fie nulă

migrare de la gon

Deoarece Moshi este mai riguros nu este recomandat să facă o migrare masivă a tuturor obiectelor pe care le aveți în Java sau Kotlin, deoarece este posibil ca modelele dvs. să nu fie bine definite.

de către Exemplu, dacă modelele dvs. au definit variabile non-nulle care, la un moment dat, nu au fost folosite niciodată, cu GSON nu veți fi niciodată sărutați eroarea și acest lucru va fi ascuns, dar dacă același model îl migrați la Moshi eroarea va sari la Moshi. Chiar și așa, există mai multe opțiuni:

  • Dacă modelul dvs. este în Java: convertirea acestuia la Kotlin cu toate atributele dvs. nulle.
  • Modelele dvs. sunt în Kotlin și codul dvs. Asta le folosește, de asemenea, în Kotlin: eliminați atributele care nu le folosesc sau le fac toate nullele.
  • Efectuați o migrare progresivă: începeți să utilizați Moshi în analiza noilor cereri și lăsați-le pe cei vechi cu GSON, dar considerând că acestea nu ar trebui amestecate în același model.
  • Efectuați Testele către modelele dvs. pentru a fi 100% siguri că sunt bine definite și, astfel, migrează fără teama de a rupe nimic.

Migrația progresivă

Prin această soluție vom avea a Adnotarea cu care vom spune Retrofit Care sunt apelurile pe care vrem să le analizăm cu Moshi.

DIV ID = „5838BBA41D”>

Pentru ca acest lucru să lucrăm, trebuie să creăm convertorul nostru. Fabrica pentru verificarea dacă un serviciu este marcat cu @moshi:

class MoshiMigrationConverter(private val moshiConverterFactory: MoshiConverterFactory) : Converter.Factory() { override fun responseBodyConverter( type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? { for (annotation in annotations) { if (annotation.annotationClass == Moshi::class) { return moshiConverterFactory.responseBodyConverter(type, annotations, retrofit) } } return null } override fun requestBodyConverter( type: Type, parameterAnnotations: Array<Annotation>, methodAnnotations: Array<Annotation>, retrofit: Retrofit): Converter<*, RequestBody>? { for (annotation in methodAnnotations) { if (annotation.annotationClass == Moshi::class) { return moshiConverterFactory.requestBodyConverter( type, parameterAnnotations, methodAnnotations, retrofit) } } return null }}

În cele din urmă o introducem la instanța noastră de modernizare:

val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(MoshiMigrationConverter(MoshiConverterFactory.create())) .addConverterFactory(GsonConverterFactory.create()) .build()

În acest fel, noile servicii pe care le implementezi în aplicațiile dvs. pot folosi Moshi, iar anticii continuă să folosească GSON și vă permit, de asemenea, să migrați unul câte unul Ervicios.

Migrația cu teste

Primul pas ar fi acela de a face un test pentru a verifica dacă definiția modelului nostru corespunde ceea ce returnează serverul. Pentru aceasta, vom avea nevoie doar de Junit și un fișier .json care este copia a ceea ce se întoarce serviciul la noi. Să punem modelul nostru anterior de mâncare și să adăugăm următorul „foo.json” în interiorul dosarului de testare a resurselor, ar fi în „Testare / resurse / foo.json”

{ "id": 0, "name": "SDOS" }

Clasa noastră de testare ar fi ca aceasta:

class FooDTOTest { private val loader = javaClass.classLoader!! private val gson = GsonBuilder().create() @Test fun parse() { val jsonString = String(loader.getResourceAsStream("foo.json").readBytes()) val actual = gson.fromJson(jsonString, FooDTO::class.java) val expected = FooDTO(10, "SDOS") assertEquals(expected, actual) }}

Variabila GSON trebuie să fie aceeași cu cea pe care o folosiți în modernizare, în cazul nu Are numar GonsBuilder (). Creați () este cel care este creat în mod implicit.

Ceea ce facem este citirea fișierului „foo.json”, spunem lui Gon să-l deserializeze în alimente și rezultatul În variabila actuală. În așteptat, am creat un obiectiv pe care sperăm că este rezultatul meserializării JSON și am verificat în cele din urmă cu Junit că ambele modele sunt egale.

Odată ce avem testele noastre, putem deja să migrăm alimente pentru ao folosi Moshi și felul nostru de testare ar fi așa:

divid id = „0c018534ca”>

ca înainte, Moshi trebuie să fie același cu modul în care utilizați în Retrofit și Moshi.builder (). Build () este cel care este creat în mod implicit. Ideea testului este încă aceeași, dar acum folosim Moshi pentru a deserializa JSON.

Prin urmare, cu atât mai multe teste cu diferite tipuri de răspunsuri de pe serverul dvs. pe care îl aveți mai bine și probabil că puteți descoperi probabil Erori în modelele dvs. înainte de a migra la Moshi.

Concluzii

Sosirea Kotlin a lăsat dovezi ale deficiențelor GSON, care pot fi rezolvate folosind noi librării, cum ar fi Moshi. Moshi este o siguranță nulă, așa că forțele este de a avea modele bine definite, evitând astfel apariția unor erori neașteptate. Este folosit în mod similar cu GSON, dar evitați adăugarea de excepții de la proguare. În plus, puteți coexista cu GSON și deveniți o migrație progresivă.

GSON a fost un bun însoțitor de călătorie, dar este timpul să folosim alte alternative. Acum aveți instrumentele de bază pentru a încerca Moshi și verificați avantajele pentru voi înșivă.

Sperăm că ați fost interesant postul nostru despre Moshi. Veți folosi sau prefera alte librării? Lăsați-ne un comentariu cu experiența sau opinia dvs. despre asta!

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *