MOSHI: Modernizando a análise JSON no Android

Para o desenvolvimento de qualquer aplicativo É necessário usar JSON para se comunicar com o back-end e, até agora, a coisa usual era para se ajudar do GSON biblioteca. Embora esta ferramenta funcione corretamente em Java, não é a melhor opção para KoTlin, porque os erros descontrolados podem aparecer. Nos últimos tempos, outras ferramentas foram desenvolvidas que permitem a mesma coisa, mas são mais seguras antes desse tipo de falha. Estamos falando de Jackson, Kotlinx.Serialization e Moshi. Embora estas três opções apresentem as mesmas vantagens sobre GSON, nos concentraremos em Moshi por ser o mais popular e o mais estável agora.

O que Moshi é uma livraria para Pause JSON em Objetos Java ou KoTlin. É desenvolvido por Quadrado para quase as mesmas pessoas que Gson. Os principais desenvolvedores, Jesse Wilson e Jake Wharton, disseram que Moshi poderia ser considerado gson v3.

As principais vantagens sobre GSON são:

  • Moshi entende e funciona corretamente Tipos nulos de KoTlin.
  • Não é mais necessário indicar ao progueiro que não oferece o pacote onde você tem seus modelos localizados, já que com a configuração básica é suficiente.
  • Moshi em desenvolvimento e melhoria, enquanto GSON é praticamente uma livraria acabada.
  • ocupa menos tamanho no APK.

** (Jesse Wilson expõe mais vantagens aqui).

Se você estiver acostinado para usar o GSON, você não terá problemas usando Moshi, pois eles parecem muito. No entanto, é necessário levar em conta algumas diferenças e tomar certas precauções ao fazer uma migração.

Usando a biblioteca

Recomenda-se usar Moshi juntos Codegen: https://github.com/square/moshi#codegen

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

e nunca esquecer Adicione as regras do progueiro: https://github.com/square/moshi/blob/master/moshi/src/main/resources/ME. ..

Para usá-lo junto com o retrofit Use este conversor: https://github.com/square/retrofit/tree/master/retrofit-converters/moshi

implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

e adicionar à sua instância de retrofit da seguinte forma:

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

Com isso você já tem o seu projeto pronto para usar o Moshi para parar o seu JSON.

Diferenças com GSON: modelos

onde com gson tivemos:

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

com a MOSHI temos:

A anotação @SerializedName alterações por @Json e porque usamos codegen, é necessário Anote todas as classes com @JsonClass(generateAdapter = true.

di Muers com GSON: desserializador / serializador

com gson seria colocado desta forma:

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)!! }}

e com a MOSHI este:

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ças com GSON: KoTlin Null Segurança

Gson não entende os tipos não nulos de KoTlin. Isso significa que, se tentarmos desserializar um valor nulo em uma taxa não nullible, Gson irá fazê-lo sem erros, o que pode causar exceções inesperadas facilmente. Por exemplo:

Dado o modelo de alimentos anteriormente, e um JSON {“ID”: 0, “nome”: null}:

  • gson criará o objeto FoodO ( 0, null), embora isso não seja possível, pois o nome foi definido como não-nullível. Não vai não lançar exceção, mas se você receber erros ao usar o nome:
    • name.dempty () irá produzir um nullpointerException
    • name.trim () lançará um tipo de typeCastException: null não pode ser Elenco para tipo não-nulo KoTlin.Chosquence
  • moshi lançará a seguinte exceção quando desserializando:
    com.squareup.moshi.JsonDataException: Required value 'name' (JSON name 'nombre') missing
  • / Ul>

    com a MOSHI, saberemos desde o primeiro momento que falha na definição de nosso modelo, no entanto, com GSON, não estaremos cientes até o momento de usar a variável e poderão ocorrer em qualquer lugar no código e Ser capaz de jogar vários tipos de erro de acordo com o uso da variável e tudo isso acontecerá enquanto você se pergunta como isso foi possível para uma variável não-nula ser nula.

    migração de GSON

    Porque Moshi é mais rigoroso não é recomendado para fazer uma migração massiva de todos os objetos que você tem em Java ou KoTlin, já que é possível que seus modelos não estejam bem definidos.

    por Exemplo, se seus modelos definiram variáveis não anuláveis que em algum momento são null, mas nunca foram usadas, com GSON você nunca terá pulado o erro e isso terá sido oculto, mas se esse mesmo modelo você migre para Moshi, o erro Vai pular para Moshi. Mesmo assim, existem várias opções:

    • se o seu modelo estiver em Java: convertê-lo para KoTlin com todos os seus atributos nullíveis.
    • Seus modelos estão em KoTlin e o código Isso os usa também está em KoTlin: Remover atributos que não usam ou tornem todos nullíveis.
    • execute uma migração progressiva: comece a usar a MOSHI na análise de novas solicitações e deixe os antigos com GSON, mas considerando que eles não devem ser misturados no mesmo modelo.
    • testes para seus modelos para ter 100% de certeza de que eles estão bem definidos e, portanto, migrar sem medo de quebrar qualquer coisa.

    migração progressiva

    por esta solução, teremos um Anotação com a qual diremos a retrofit quais são as chamadas que queremos analisar com a Moshi.

    @Target(AnnotationTarget.FUNCTION)@Retention(RUNTIME)annotation class Moshiinterface GdaxApi { @GET("products") fun products(): List<Product> @Moshi @GET("products") fun productsMoshi(): List<Product>}

    Para isso, temos que criar nosso próprio conversor. fábrica para verificar se um serviço for marcado com @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 }}

    Finalmente, inserimos-o à nossa instância de Retrofit:

    Desta forma, os novos serviços que você estão implementando em seus aplicativos podem usar a MOSHI e os antigos continuarem usando GSON e também permite migrar um por um Ervicios.

    Migração com testes

    O primeiro passo seria fazer um teste para verificar se a definição do nosso modelo corresponde ao que retorna o servidor. Para isso, só precisaremos do JUnit e de um arquivo .json que é a cópia do que o serviço retorna a nós. Vamos colocar nosso modelo de comida anterior e adicionar o seguinte “foo.json” dentro da pasta de recursos de teste, seria em “teste / recursos / foo.json”

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

    Nossa classe de teste seria assim:

    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) }}

    A variável GSON deve ser a mesma que você usa em retrofit, no caso de não Tem um número gonsbuilder (). Create () é aquele que é criado por padrão.

    O que estamos fazendo é ler o arquivo “foo.json”, nós dizemos que Gon to desserializá-lo no alimento e no resultado Na variável atual. No esperado, criamos um objetivo que esperamos que seja o resultado de desserializar o JSON e nós finalmente verificamos com JUnit que ambos os modelos são iguais.

    Uma vez que temos nossos testes, já podemos migrar alimentos para usá-lo com Moshi e nosso tipo de teste seria assim:

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

    Como antes, a Moshi deve ser o mesmo que você usa em Retrofit e Moshi.Builder (). Build () é aquele que é criado por padrão. A ideia do teste ainda é a mesma, mas agora usamos moshi para desserializar o JSON.

    Portanto, quanto mais testes com diferentes tipos de respostas do seu servidor você tem melhor e você provavelmente pode descobrir Erros em seus modelos antes de migrar para Moshi.

    Conclusões

    A chegada de Kotlin deixou evidências de deficiências GSON, que podem ser resolvidas usando novas livrarias, como a Moshi. A Moshi é uma segurança nula, por isso as forças têm modelos bem definidos, evitando assim o surgimento de erros inesperados. É usado de forma semelhante ao GSON, mas evite adicionar exceções ao proguard. Além disso, você pode coexistir com GSON e se tornar uma migração progressiva.

    Gson tem sido um bom companheiro de viagem, mas é hora de usar outras alternativas. Agora você tem as ferramentas básicas para experimentar Moshi e verificar suas vantagens para si mesmas.

    Esperamos que tenha sido interessante nosso post sobre Moshi. Você vai usar ou preferir outras livrarias? Deixe-nos um comentário com sua experiência ou opinião sobre isso!

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *