【问题标题】:retrofit + gson deserializer: return inside array改造 + gson 反序列化器:返回内部数组
【发布时间】:2016-09-04 07:07:48
【问题描述】:

我有返回 json 的 api:

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

我写模型类(Kotlin lang)

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

我想使用 retoft 从 json 中的“国家”字段返回 List 的请求

我接着写:

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,但在第 1 行第 2 列路径 $ 处为 BEGIN_OBJECT CountryDeserializer 代码甚至不执行! 他们想从我这里得到什么?

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

我不想像这样使用模型类

class Countries {
   public List<Country> countries;
}

【问题讨论】:

  • 为什么你不想使用data class Countries(val countries:List&lt;Country&gt; = listOf&lt;Country&gt;())
  • 我认为这是不必要的实体
  • 那为什么不隐藏它而不是反序列化呢?即interface DictService { @GET("/json/countries") public fun _countries(): Observable&lt;Countries&gt; } data class Countries(val countries:List&lt;Country&gt; = listOf&lt;Country&gt;()) fun DictService.countries() = _countries().map { it.countries } ?

标签: json gson retrofit kotlin


【解决方案1】:

我找到了路:

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

但我认为,对于这样的问题,这是一个非常复杂的决定。 还有另一种更简单的方法吗?

另一个: 我从开始消息中发现我的初始代码中有错误!!! 用ArrayList替换List就可以了!

【讨论】:

    【解决方案2】:

    我会使用 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)
    

    【讨论】:

    • 这不会打开 countries 似乎是作者意图的项目
    【解决方案3】:

    如果您的意图是简化界面并隐藏中间包装对象,我想最简单的做法是向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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-28
      • 2016-02-21
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      相关资源
      最近更新 更多