4

Retrofit Instance:

    fun getMoshi() : Moshi{
        return Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()
    }


    fun retrofit(baseUrl: String): Retrofit = Retrofit.Builder()
        .client(getClient())
        .baseUrl(baseUrl)
        .addConverterFactory(MoshiConverterFactory.create(getMoshi()).asLenient())
        .build()

}

BaseValidation model:

    @JsonClass(generateAdapter = true)
    data class BaseValidation (
        val status : String,
        val data : List<Data>
    )

response parsing:

   val type: Type = Types.newParameterizedType(String::class.java, Data::class.java)
    val moshi = Moshi.Builder().build()
    val jsonAdapter: JsonAdapter<Map<String,Data>> = moshi.adapter(type)
    val baseValidation = jsonAdapter.fromJson(response.errorBody()!!.charStream().toString())!!

results in:

Platform class java.lang.String in java.lang.String<com.example.vow.data_remote.model.create.response.validation.Data> requires explicit JsonAdapter to be registered

I am new to Moshi and kind of confused how to parse correctly, I've managed to achieve the result with gson using code below, but I want to learn how it's done with Moshi:

val gson = Gson()
val type = object : TypeToken<BaseValidation>() {}.type
var errorResponse: BaseValidation? = gson.fromJson(response.errorBody()!!.charStream(), type)

is the Type below defined correctly?

val type: Type = Types.newParameterizedType(String::class.java, Data::class.java)

UPDATE

val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val jsonAdapter: JsonAdapter<BaseValidation> = moshi.adapter(BaseValidation::class.java).lenient()
val baseValidation = jsonAdapter.fromJson(response.errorBody()!!.charStream().toString())!!

returns

com.squareup.moshi.JsonEncodingException: Use JsonReader.setLenient(true) to accept malformed JSON at path $

When moshi set to lenient() by

moshi.adapter(BaseValidation::class.java).lenient()

i get

Expected BEGIN_OBJECT but was STRING at path $

OkHttp response is

{
    "status": "validation",
    "data": [
        {
            "type": "Email",
            "code": 3000,
            "field": "User.contact.email",
            "message": "Email is invalid"
        }
    ]
}

SOLUTION

maybe it will help someone in the future

 response.errorBody()!!.charStream().toString()

instead should be

response.errorBody()?.source()
ashaneen
  • 137
  • 1
  • 15
  • I am actually curious if it's possible to not create a data class that will retrieve status and data properties but instead, access the data array property right away. the status in my case is always success, so there's no point creating a data class that will hold the status and data list. thoughts? – chitgoks Dec 14 '20 at 08:08
  • I see you're using Retrofit with `KotlinJsonAdapterFactory` and `MoshiConverterFactory`, but `MoshiConverterFactory` is in Retrofit converter-moshi library and whereas `KotlinJsonAdapterFactory` is in moshi-kotlin dependency and when I try to add both of them, there is a warning that there are duplicate classes. How did you solved this? Excluding something from Retrofit maybe? – Juan José Melero Gómez Nov 09 '21 at 22:42

1 Answers1

2

If you want to use Moshi with Kotlin reflection you have to don't forget to add KotlinJsonAdapterFactory. This will work fine I guess:

val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()
val jsonAdapter = moshi.adapter(BaseValidation::class.java)
val baseValidation = jsonAdapter.fromJson(response.errorBody()!!.charStream().toString())!!

also, you can remove @JsonClass annotation from your classes. It's not needed for while you are using reflection.

Andrei Tanana
  • 7,932
  • 1
  • 27
  • 36