2016-05-09 20 views
0

我有API返回JSON:改造+ GSON解串器:返回内部数组

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

我写模型类(科特林郎)

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

而且我想用retorift那久违的列表请求做< 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列路径$ CountriesDeserializer代码不执行甚至! 他们想要我什么?

也许我需要写我自己的TypeAdapterFactory?

我不想使用模型类像

class Countries { 
    public List<Country> countries; 
} 
+0

为什么你不想使用'数据类国家(VAL国家:名单 = listOf ())'? – miensol

+0

我认为这是不必要的实体 – Dmitry

+0

那么为什么不隐藏它而不是与反序列化作斗争呢?即'接口 DictService { @GET( “/ JSON /国家”) 公共乐趣_countries():可观察 } 数据类国家(VAL国家:名单 = listOf ()) 乐趣DictService.countries ()= _countries()。map {it.countries} '? – miensol

回答

0

我找到了办法:

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的错误! 它工作正常,如果替换ListList ArrayList!

0

我会用Jackson来完成这个任务。 试试这个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) 
+1

这不会展开似乎是作者意图的“国家”项目 – miensol

2

如果你的目的是为了简化接口和隐藏中间包装对象,我想以最简单的做法是将扩展方法添加到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()