retrofit + gson deserializer: возврат внутри массива

У меня есть api, который возвращает json:

{"countries":[{"id":1,"name":"Australia"},{"id":2,"name":"Austria"}, ... ]} 

Я пишу модельный класс (Kotlin lang)

 data class Country(val id: Int, val name: String) 

И я хочу сделать запрос с помощью retorift, который возвращает List <Models.Country>, из поля «страны» в json

Я пишу следующий:

 interface DictService { @GET("/json/countries") public fun countries(): Observable<List<Models.Country>> companion object { fun create() : DictService { val gsonBuilder = GsonBuilder() val listType = object : TypeToken<List<Models.Country>>(){}.type gsonBuilder.registerTypeAdapter(listType, CountriesDeserializer) gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) val service = Retrofit.Builder() .baseUrl("...") .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gsonBuilder.create())) .build() return service.create(DictService::class.java) } } object CountriesDeserializer : JsonDeserializer<List<Models.Country>> { override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): List<Models.Country>? { val res = ArrayList<Models.Country>() if(json!=null) { val countries = json.asJsonObject.get("countries") if (countries.isJsonArray()) { for (elem: JsonElement in countries.asJsonArray) { res.add(Gson().fromJson(elem, Models.Country::class.java)) } } } return null; } } } 

Но я получаю ошибку: java.lang.IllegalStateException: ожидается BEGIN_ARRAY, но BEGIN_OBJECT в строке 1 столбец 2 путь $ СтраныDeserializer код даже не выполняется! Что они хотят от меня?

Может быть, мне нужно написать свой собственный TypeAdapterFactory?

Я не хочу использовать класс модели, например

 class Countries { public List<Country> countries; } 

Если вы намерены упростить интерфейс и скрыть промежуточный объект-оболочку, я думаю, что самое простое – добавить метод расширения в DictService так:

 interface DictService { @GET("/json/countries") fun _countries(): Observable<Countries> } fun DictService.countries() = _countries().map { it.countries } data class Countries(val countries: List<Country> = listOf()) 

Которые можно использовать следующим образом:

 val countries:Observable<List<Country>> = dictService.countries() 

Я нашел способ:

 object CountriesTypeFactory : TypeAdapterFactory { override fun <T : Any?> create(gson: Gson?, type: TypeToken<T>?): TypeAdapter<T>? { val delegate = gson?.getDelegateAdapter(this, type) val elementAdapter = gson?.getAdapter(JsonElement::class.java) return object : TypeAdapter<T>() { @Throws(IOException::class) override fun write(outjs: JsonWriter, value: T) { delegate?.write(outjs, value) } @Throws(IOException::class) override fun read(injs: JsonReader): T { var jsonElement = elementAdapter!!.read(injs) if (jsonElement.isJsonObject) { val jsonObject = jsonElement.asJsonObject if (jsonObject.has("countries") && jsonObject.get("countries").isJsonArray) { jsonElement = jsonObject.get("countries") } } return delegate!!.fromJsonTree(jsonElement) } }.nullSafe() } } 

Но это очень сложное решение, я думаю, для такой проблемы. Есть ли еще один простой способ?

Еще один: я обнаружил ошибку в своем исходном коде с начала meassage !!! Он отлично работает, если заменить List на ArrayList!

Я бы использовал Джексона для этой задачи. Попробуйте это https://github.com/FasterXML/jackson-module-kotlin

 val mapper = jacksonObjectMapper() data class Country(val id: Int, val name: String) // USAGE: val country = mapper.readValue<Country>(jsonString)