2

I have the following data classes to parse JSON. I can parse it easily with the decodeFromString method. However, the Info classes could contain the List<Int> type from time to time along with the Int type so that both are included in a single JSON. How can I handle this variation in serialization?

@Serializable
data class Node (@SerialName("nodeContent") val nodeContent: List<Info>)
@Serializable
data class Info (@SerialName("info") val info: Int)

p.s. The closest question to mine is this one: Kotlinx Serialization, avoid crashes on other datatype. I wonder if there are other ways?

EDIT: An example is given below.

"nodeContent": [
    {
        "info": {
            "name": "1",
        },
    },
    {
        "info": [
            {
                "name": "1"
            },
            {
                "name": "2"
            },
        ],
    },
    {
        "info": {
            "name": "2",
        },
    }
]
Erdem Tuna
  • 500
  • 4
  • 18

1 Answers1

3

Here is an approach with a custom serializer similar to the link you provided. The idea is to return a list with just a single element.

// Can delete these two lines, they are only for Kotlin scripts
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0")
@file:CompilerOptions("-Xplugin=/snap/kotlin/current/lib/kotlinx-serialization-compiler-plugin.jar")

import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.encoding.Decoder

@Serializable
data class Node (val nodeContent: List<Info>)
@Serializable(with = InfoSerializer::class)
data class Info (val info: List<Name>)
@Serializable
data class Name (val name: Int)


@Serializer(forClass = Info::class)
object InfoSerializer : KSerializer<Info> {
    override fun deserialize(decoder: Decoder): Info {
        val json = ((decoder as JsonDecoder).decodeJsonElement() as JsonObject)
        return Info(parseInfo(json))
    }

    private fun parseInfo(json: JsonObject): List<Name> {
        val info = json["info"] ?: return emptyList()
        return try {
            listOf(Json.decodeFromString<Name>(info.toString()))
        } catch (e: Exception) {
            (info as JsonArray).map { Json.decodeFromString<Name>(it.toString()) }
        }
    }
}

Usage:

val ss2 = """
{
    "nodeContent": [
        {
            "info":
                {"name": 1}
        },
        {
            "info": [
                {"name": 1},
                {"name": 2}
            ]
        },
        {
            "info":
                {"name": 2}
        }
    ]
}
"""

val h = Json.decodeFromString<Node>(ss2)
println(h)

Result:

Node(nodeContent=[Info(info=[Name(name=1)]), Info(info=[Name(name=1), Name(name=2)]), Info(info=[Name(name=2)])])
xjcl
  • 12,848
  • 6
  • 67
  • 89
  • Can you include implementation for `descriptor: SerialDescriptor` and `fun serialize(encoder: Encoder, value: AttributeValue)` ? – iadcialim24 May 30 '23 at 06:34