【问题标题】:More efficient way to model JSON data using retrofit - Kotlin使用改造对 JSON 数据建模的更有效方法 - Kotlin
【发布时间】:2021-10-25 03:18:48
【问题描述】:

我要使用的数据有这样的结构:

{
    "1": {
        "id": 1,
        "name": "Bulbasaur"
    },
    "2": {
        "id": 2,
        "name": "Ivysaur"
    },
    "3": {
        "id": 3,
        "name": "Venusaur"
    }
}

注意: 标记每个对象的数字与神奇宝贝的 id 匹配,而不是神奇宝贝的数量

我的问题是,当我尝试为此创建数据类时,它最终会为每个对象创建一个数据类。没有一个数据类适合每个对象。我相信这是由于标记对象(神奇宝贝)的数字对于每个对象都不同。

有没有一种方法可以将这些数据格式化为一个或两个数据类,而不是超过 800 个?

理想情况下,我希望数据具有这样的结构,但运行时不起作用。

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

【问题讨论】:

  • 如果我的评论能帮助你解决问题,希望你能接受我的回答。谢谢!

标签: android json kotlin retrofit


【解决方案1】:

当使用这种特殊情况将 Json 解析为 Object 时,您应该自己自定义 Json Deserializer。

这里我使用 Gson 库将 Json 解析为 Object。

首先,使用 Gson 创建一个自定义的 Json Deserializer。如下:

PokemonResponse.kt

data class PokemonResponse(
    val pokemonMap: List<StringReleasedPokemonModel>
)

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

GsonHelper.kt

object GsonHelper {
    fun create(): Gson = GsonBuilder().apply {
        registerTypeAdapter(PokemonResponse::class.java, PokemonType())
        setLenient()
    }.create()

    private class PokemonType : JsonDeserializer<PokemonResponse> {
        override fun deserialize(
            json: JsonElement?,
            typeOfT: Type?,
            context: JsonDeserializationContext?
        ): PokemonResponse {
            val list = mutableListOf<ReleasedPokemonModel>()
            // Get your all key
            val keys = json?.asJsonObject?.keySet()
            keys?.forEach { key ->
                // Get your item with key
                val item = Gson().fromJson<ReleasedPokemonModel>(
                    json.asJsonObject[key],
                    object : TypeToken<ReleasedPokemonModel>() {}.type
                )
                list.add(item)
            }
            return PokemonResponse(list)
        }
    }
}

接下来我将创建一个GsonConverterFactory,以便我可以addConvertFactory 进行改造。

val gsonConverterFactory = GsonConverterFactory.create(GsonHelper.create())

现在我将添加改造。

val retrofit = Retrofit.Builder()
    // Custom your Retrofit
    .addConverterFactory(gsonConverterFactory) // Add GsonConverterFactoty
    .build()

最后在 ApiService 中,您的响应现在将返回类型 PokemonResponse

interface ApiService {
    @GET("your_link")
    suspend fun getGenres(): PokemonResponse
}

【讨论】:

    【解决方案2】:

    问题是那里没有 JSON 数组。它实际上是一个 JSON 对象,每个口袋妖怪都被列为一个属性。我建议您事先重新格式化 JSON,使其看起来像这样:

    [
        {
            "id": 1,
            "name": "Bulbasaur"
        },
        {
            "id": 2,
            "name": "Ivysaur"
        },
        {
            "id": 3,
            "name": "Venusaur"
        }
    ]
    

    然后你可以这样建模:

    data class ReleasedPokemonModel(
        val id: Int,
        val name: String
    )
    
    data class Response(
        val items: List<ReleasedPokemonModel>
    )
    

    See more here.

    And see here for discussion about reformatting the data before handing it to Retrofit.

    【讨论】:

      【解决方案3】:

      您可以使用 Map 来存储密钥,如下所示

      data class PokemonResponse(
          val pokemonMap:Map<String,ReleasedPokemonModel>
      )
      
      data class ReleasedPokemonModel(
          val id: Int,
          val name: String
      )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-03
        • 1970-01-01
        • 1970-01-01
        • 2014-07-22
        • 1970-01-01
        相关资源
        最近更新 更多