1

I have a Kotlin data class that has an arg that can either be an Object or Array. Is there a way to de-serialize a string into this class and not care if not an Array but somehow get it into an array of one?

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

data can come back as both ways a single object or an array of objects( I have no control over the data as it is 3rd party data.

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)
3

3 Answers 3

3

You have to write a custom JsonDeserializer. Both or your class should have the same parent class. Then, write a custom JsonDeserializer for this specific type.

For example:

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

Then, in your GsonBuilder you have to register this custom JsonDeserializer: gsonBuilder.registerTypeAdapter(GameParent::class.java, GameDeserializer());

Now, whenever your Gson expect GameParent will use registered deserializer.

Sign up to request clarification or add additional context in comments.

Comments

2

my suggestions for solving your task

my solution if name is object, replace it with arrays

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

version 1 -- created and registry adapter 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
    }
}

version 2 -- manipulating the response

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

6 Comments

IMHO registering a TypeAdapter would be more promising than manipulating the response.
@YasinKaçmaz I add TypeAdapter for me, both solutions are ugly
@YasinKaçmaz I created version 1 and vesrion 3 vesrion 3 what do you think about this solution
type token would be good if "game" is a response object. But yes we can use type token too.
I like the third one as it creates the array of its an object but also doesn't make me write a different class for the data.
|
0

I found a quick and sort solution for it by using the param as Any in the model class and then serialize it.

   private fun serializableToList(obj: Any): List<Pojo> {
    val pojoList: MutableList<Pojo> = mutableListOf()
    when (obj) {
        is List<*> -> {
            obj.forEach {
                val pojoObject = Gson().fromJson(Gson().toJson(it), Pojo::class.java)
                pojoList.add(pojoObject)
            }
        }

        else -> {
            val pojoObject = Gson().fromJson(Gson().toJson(obj), Pojo::class.java)
            pojoList.add(pojoObject)
        }
    }
    return pojoList
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.