【问题标题】:Data Class Either Object or Array数据类对象或数组
【发布时间】:2019-03-05 05:23:17
【问题描述】:

我有一个 Kotlin 数据类,它的 arg 可以是对象或数组。有没有办法将一个字符串反序列化到这个类中,而不关心是否不是一个数组,而是以某种方式将它变成一个数组?

data class Game(var name:List<NameItem>)
data class NameItem(var title: String, var id: Int)

数据可以以单个对象或对象数组的两种方式返回(我无法控制数据,因为它是第 3 方数据。

jsonString = "{"game":{"name":{"title":"GameName","id":22}}}"
jsonString = "{"game":{"name":[{"title":"GameName","id":22},{"title":"GameName2","id":23}]}}"

game: Game? = Gson().fromJson(jsonString  Game::class.java)

【问题讨论】:

标签: arrays json kotlin gson


【解决方案1】:

您必须编写自定义JsonDeserializer。两者或您的班级应该具有相同的父班级。然后,为此特定类型编写自定义JsonDeserializer

例如:

sealed class GameParent() {
    data class Game(val name: String): GameParent()
    data class GameList(val games: List<Game>): GameParent()
}

class GameDeserializer : JsonDeserializer<GameParent> {
    override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): GameParent {
        with(json) {
           if(isJsonObject) {
             // parse as Game
           }

           if(isJsonArray) {
             // parse as GameList
           }
        }
    }
}

然后,在您的GsonBuilder 中,您必须注册此自定义JsonDeserializergsonBuilder.registerTypeAdapter(GameParent::class.java, GameDeserializer());

现在,只要您的 Gson 期望 GameParent 将使用已注册的解串器。

【讨论】:

    【解决方案2】:

    我对解决你的任务的建议

    如果名称是对象,我的解决方案,用数组替换它

    data class Game(var name:List<NameItem> )
    data class NameItem(var title: String, var id: Int)
    
    
    
    fun main(args: Array<String>) {
        var json = "{\"game\":{\"name\":[{\"title\":\"game 1\",\"id\":1},{\"title\":\"game 2\",\"id\":2}]}}"
        println(useJsonParser(json))    //Game(name=[NameItem(title=game 1, id=1), NameItem(title=game 2, id=2)])
        json = "{\"game\":{\"name\":[{\"title\":\"game 1\",\"id\":1}]}}"
        println(useJsonParser(json))    //Game(name=[NameItem(title=game 1, id=1), NameItem(title=game 2, id=2)])
        json = "{\"game\":{\"name\":{\"title\":\"game 1\",\"id\":1}}}" // not array
        println(useJsonParser(json))    //Game(name=[NameItem(title=game 1, id=1)])
    }
    

    版本 1 -- 创建和注册适配器 link @Cililing

    fun useJsonParser(json: String): Game? {
        val gson = GsonBuilder().registerTypeAdapter(Game::class.java, GameDeserializer()).create()
        return gson.fromJson(json, Game::class.java)
    }
    
    class GameDeserializer : JsonDeserializer<Game?> {
        override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Game? {
            val gameJson = json!!.asJsonObject.get("game")
            if (gameJson.isJsonObject) {
                val jsonName = gameJson.asJsonObject["name"]
                val list = if (jsonName.isJsonObject) {
                    arrayOf(Gson().fromJson(jsonName, NameItem::class.java))
                } else {
                    val fromJson = Gson().fromJson(jsonName, Array<NameItem>::class.java)
                    fromJson
                }.toList()
                return Game(list)
            }
            return null
        }
    }
    

    版本 2 -- 处理响应

    fun useJsonParser(json:String):Game?{
        val jsonObject = JsonParser().parse(json).asJsonObject.get("game")
        if(jsonObject.asJsonObject["name"].isJsonObject){
            val jsonName = jsonObject.asJsonObject["name"].asJsonObject
            val array = JsonArray()
            array.add(jsonName)
            jsonObject.asJsonObject.add("name", array) // rewrite origin JSON
        }
    
        return Gson().fromJson(jsonObject, Game::class.java)
    
    }
    

    vesrion 3 -- 添加适配器 TypeToken>()

    fun useJsonParser(json: String): Game? {
        val type = object : TypeToken<MutableList<NameItem>>() {}.type
        val gson = GsonBuilder().registerTypeAdapter(type, NameItemDeserializer()).create()
        return gson.fromJson(JsonParser().parse(json).asJsonObject.get("game"), Game::class.java)
    }
    
    class NameItemDeserializer : JsonDeserializer<List<NameItem>?> {
        override fun deserialize(json: JsonElement, type: Type, context: JsonDeserializationContext?): List<NameItem>? {
            with(json){
                return if(isJsonObject){
                    arrayListOf(Gson().fromJson(this,NameItem::class.java))
                }else{
                    Gson().fromJson(this,Array<NameItem>::class.java).toList()
                }
            }
        }
    }
    

    【讨论】:

    • 恕我直言,注册TypeAdapter 比操纵响应更有希望。
    • @YasinKaçmaz 我为我添加了 TypeAdapter,两种解决方案都很丑
    • @YasinKaçmaz 我创建了版本 1 和 vesrion 3 vesrion 3 你觉得这个解决方案怎么样
    • 如果"game" 是响应对象,类型标记会很好。但是是的,我们也可以使用类型标记。
    • 我喜欢第三个,因为它创建了一个对象的数组,但也不会让我为数据编写不同的类。
    猜你喜欢
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 2012-05-08
    相关资源
    最近更新 更多