How to create a custom Moshi JsonAdapter that returns a Map?

29 Views Asked by At

I'm using an API that returns the following data:

{
    "ad": "Andorra",
    "ae": "United Arab Emirates",
    "af": "Afghanistan",
    "ag": "Antigua and Barbuda",
    "ai": "Anguilla",
    "al": "Albania",
    "am": "Armenia",
    // ..... rest of JSON
}

As you can see, the JSON is basic: all properties are required to have String as value. My question is: is it possible to create a custom adapter that'll read all the fields + values, add them to a map and return just the map?

In other words, return something like:

val expectedMap = mapOf("ad" to "Andorra", "ae" to "United Arab Emirates", "af" to "Afghanistan", /* all other fields here */)

What I've made so far is:

class CountriesAdapter {

    @FromJson
    fun fromJson(reader: JsonReader): Map<String, String> {
        val countries = mutableMapOf<String, String>()

        with(reader) {
            beginObject()
            while (hasNext()) {
                countries[nextName()] = nextString()
            }
            endObject()
        }
        return countries
    }

    @ToJson
    fun toJson(p0: JsonWriter, p1: Countries?): String {
        return ""
    }
}

I'm adding this adapter to my Moshi instance, but I can't figure out how to make it work with Retrofit, currently I have the following code:

val moshi = Moshi.Builder()
    .add(CountriesAdapter())
    .addLast(KotlinJsonAdapterFactory())
    .build()
val retrofit = Retrofit.Builder()
    .baseUrl("https://flagcdn.com")
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()
    .create(APIInterface::class.java)

retrofit.getCountries().enqueue(
    object : Callback<Countries> {
        override fun onResponse(p0: Call<Countries>, p1: Response<Countries>) {
            if (p1.isSuccessful) {
                // Not sure how to make my adapter return my Map here.
                _countries.value = CountriesAdapter(moshi).fromJson(p1.??)
            }
        }

        override fun onFailure(p0: Call<Countries>, p1: Throwable) {
            /* do nothing yet */
        }
    }
)

data class Countries(
    val field: String,
)

Is it possible to achieve what I have in mind?

0

There are 0 best solutions below