Search code examples
androidretrofit2converters

Android retrofit response: from parameters of object to Map<String, String>


Got retrofit response like this from currency Api: enter image description here

Using the "data class from json" plugin will create a data class for "result" with currencies as parametеr:

enter image description here enter image description here

I can add all currencies(more than 170+) to this class as params. But if the list of currencies will change, I needed to change classes too.

Maybe we can somehow convert "results" from object "Result" to Map<String, String> ?

enter image description here

List of currencies can be massive:

enter image description here

retrofit creation:

    @Provides
@Singleton
fun provideRemoteClient(): Retrofit {
    return Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .client(provideHttpClient())
        .build()
}

httpClient:

    @Provides
@Singleton
fun provideHttpClient(): OkHttpClient {
    val loggingInterceptor = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    }

    val queryInterceptor = Interceptor { chain ->
        val original: Request = chain.request()
        val originalHttpUrl: HttpUrl = original.url
        val url = originalHttpUrl.newBuilder()
            .addQueryParameter("api_key", Constants.API_KEY)
            .build()

        val requestBuilder: Request.Builder = original.newBuilder()
            .url(url)
        val request: Request = requestBuilder.build()
        chain.proceed(request)
    }

    return OkHttpClient.Builder()
        .addInterceptor(loggingInterceptor)
        .addInterceptor(queryInterceptor)
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .build()
}

Solution

  • correct answer is here: How to convert similar Json objects to Json Arrays with Gson and Retrofit

    We need to create custom json-deserializer class and parse object as entrySet.

    for my example this class will be like this:

    class FetchResultDeserializer : JsonDeserializer<FetchApiResponse> {
    override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): FetchApiResponse {
        if (json == null || context == null) {
            throw Exception("deserialize Error")
        }
        val obj = json.asJsonObject
    
        val base = context.deserialize<String?>(obj.get("base"), String::class.java)
        val resultSet = obj.get("results").asJsonObject.entrySet()
        val resultList = resultSet.map {
            val code = it.key
            val value = it.value.asString
            Currency(code, value)
        }
        val updated = context.deserialize<String?>(obj.get("updated"), String::class.java)
        val ms = context.deserialize<Int?>(obj.get("ms"), Int::class.java)
    
        return FetchApiResponse(base = base, ms = ms, results = resultList, updated = updated)
    }}
    
    data class FetchApiResponse(
    val base: String? = null,
    val ms: Int? = null,
    val results: List<Currency>  ? = null,
    val updated: String  ? = null)
    
    data class Currency(val name:String, val value: String)
    

    as result we got list, not a object:

    enter image description here