Написать класс для работы с Retrofit

У меня есть ответ в виде json

    {
    "Date": "2020-09-19T11:30:00+03:00",
    "PreviousDate": "2020-09-18T11:30:00+03:00",
    "PreviousURL": "\/\/www.cbr-xml-daily.ru\/archive\/2020\/09\/18\/daily_json.js",
    "Timestamp": "2020-09-18T17:00:00+03:00",
    "Valute": {
        "AUD": {
            "ID": "R01010",
            "NumCode": "036",
            "CharCode": "AUD",
            "Nominal": 1,
            "Name": "Австралийский доллар",
            "Value": 54.9008,
            "Previous": 54.8842
        },
        "AZN": {
            "ID": "R01020A",
            "NumCode": "944",
            "CharCode": "AZN",
            "Nominal": 1,
            "Name": "Азербайджанский манат",
            "Value": 44.1624,
            "Previous": 44.2579
        },
        .....<очень много различной валюты>....
        "JPY": {
            "ID": "R01820",
            "NumCode": "392",
            "CharCode": "JPY",
            "Nominal": 100,
            "Name": "Японских иен",
            "Value": 71.7459,
            "Previous": 71.7672
        }
    }}

Я работал с retrofit до этого, но не понимаю, как сделать так, чтобы не создавать для каждого вида валюты свой класс. Сейчас моя структура выглядит так:

    class Course{
    val Date: String? = null
    val PreviousDate : String? = null
    val PreviousURL : String? = null
    val Timestamp: String? = null
    val Valute: List<Money?>?=null
    }
    data class Money (
    val ID:String,
    val NumCode:String,
    val CharCode:String,
    val Nominal: String,
    val Name:String,
    val Value:Double,
    val Previous:Double
)

У меня появляется ошибка

Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 6 column 16 path $.Valute

скорее всего ошибка в том, как я представляю Valute. Но что именно неправильно, я не понимаю.

Воспользовался сейчас онлайн конвертером в POJO ,это помогло прояснить ситуацию. Вопрос только в том, можно ли не создавать отдельный класс для каждой валюты (типа класс AZN и AUD отличаются только названиями, все поля у них одинаковые)?


Ответы (1 шт):

Автор решения: Alex_Skvortsov

Возьмем структуру наподобие Вашей, но с мЕньшим числом полей (для краткости примера).

"Valute": {
        "AUD": {
            "ID": "R01010"
        },
        "AZN": {
            "ID": "R01020A"
        },
        .....<очень много различной валюты>....
        "JPY": {
            "ID": "R01820"
        }
    }

Создадим data class для хранения этой структуры:

data class Valute (
    val valutes: Map<String, ValuteInfo>
)

data class ValuteInfo (
    val ID : String
)

Создадим кастомный десериализатор:

class ValuteDeserializer : JsonDeserializer<Valute> {

    private val defaultGson = Gson()

    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): Valute? {
        val obj = json?.asJsonObject
        val entry = obj?.entrySet()?.iterator()?.next() ?: return null

        val values = mutableMapOf<String, ValuteInfo>()

        entry.value.asJsonObject.entrySet().forEach {
            values[it.key] = defaultGson.fromJson(it.value, ValuteInfo::class.java)
        }

        return Valute(values)
    }
}

И создаем Gson через билдер:

val gson = GsonBuilder()
            .registerTypeAdapter(Valute::class.java, ValuteDeserializer())
            .create()

Пример использования:

    val jsonString = "{\"Valute\": {\n" +
        "        \"AUD\": {\n" +
        "            \"ID\": \"R01010\"\n" +
        "        },\n" +
        "        \"AZN\": {\n" +
        "            \"ID\": \"R01020A\"\n" +
        "        },\n" +
        "        \"JPY\": {\n" +
        "            \"ID\": \"R01820\"\n" +
        "        }\n" +
        "    }}"

    val gson = GsonBuilder()
            .registerTypeAdapter(Valute::class.java, ValuteDeserializer())
            .create()

        val valute = gson.fromJson(jsonString, Valute::class.java)
        println(valute)

→ Ссылка