retroajuste + desnerializador gson: retorno dentro de la matriz

Tengo una API que me devuelve json:

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

Escribo class de model (Kotlin Lang)

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

Y quiero hacer una request usando retorift que devuelva List <Models.Country>, desde el campo "countries" en json

Escribo a continuación:

 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; } } } 

Pero obtengo el error: java.lang.IllegalStateException: BEGIN_ARRAY esperado, pero era BEGIN_OBJECT en la línea 1, columna 2 ruta $ Código de Countries Exporter no se ejecuta ni siquiera. ¿Qué es lo que quieren de mí?

Tal vez necesito escribir mi propio TypeAdapterFactory?

No quiero usar la class de model como

 class Countries { public List<Country> countries; } 

Si su intención es simplificar la interfaz y ocultar el object contenedor intermedio, supongo que lo más simple es agregar un método de extensión a DictService la DictService manera:

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

Que luego se puede usar de la siguiente manera:

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

Encontré el path:

 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() } } 

Pero es una decisión muy compleja, creo, para tal problema. ¿Hay alguna otra manera más simple?

Otro: ¡encontré error en mi código inicial de la medición de inicio !!! ¡Funciona bien si reemplaza List by ArrayList!

Usaría Jackson para esta tarea. Pruebe esto 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) 
  • IncompatibleClassChangeError: Class 'java.lang.VirtualMachineError' no implementa la interfaz 'java.lang.CharSequence'
  • Android Retrofit Get Query codificado
  • Rxjava retrofit parse api error para el usuario
  • ¿Cómo usar types personalizados para los parameters de Retrofit @Query?