OK200
OK200

Reputation: 757

Kotlinx Serialization, avoid crashes on other datatype

I'm using external API in the app, while deserialisation is done with Kotlinx Serialization package, i'm facing issues when api result is Array of Int for multiple values and primitive int for single value. How can i avoid crash in this process. Is there better approach to avoid crashes or creating data classes

ex:

import kotlinx.serialization.Serializable

@Serializable

data class Bookings (val slots: List<Int>)

when slots is having single value API returns {slots: 1} when slots is having multiple value API return { slots: [1,2,3,4]}

Upvotes: 4

Views: 1006

Answers (2)

xjcl
xjcl

Reputation: 15309

I updated @Andrei's answer for 2021 since the class and method names have changed a bit since 2019:

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

@Serializable(with = BookingsSerializer::class)
data class Bookings(val slots: List<Int>)

@Serializer(forClass = Bookings::class)
object BookingsSerializer : KSerializer<Bookings> {
    override fun deserialize(decoder: Decoder): Bookings {
        val json = (decoder as JsonDecoder).decodeJsonElement().jsonObject
        return Bookings(parseSlots(json))
    }

    private fun parseSlots(json: JsonObject): List<Int> {
        val slotsJson = json["slots"] ?: return emptyList()
        return try {
            slotsJson.jsonArray.map { it.jsonPrimitive.int }
        } catch (e: Exception) {
            listOf(slotsJson.jsonPrimitive.int)
        }
    }
}

val json = """{"slots": 1}"""
val result = Json.decodeFromString<Bookings>(json)
println(result.toString()) // prints Bookings(slots=[1])

Upvotes: 4

Andrei Tanana
Andrei Tanana

Reputation: 8432

It can be done with custom serializer:

import kotlinx.serialization.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonInput
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.int

@Serializable(with = BookingsSerializer::class)
data class Bookings(val slots: List<Int>)

@Serializer(forClass = Bookings::class)
object BookingsSerializer : KSerializer<Bookings> {
    override fun deserialize(decoder: Decoder): Bookings {
        val json = (decoder as JsonInput).decodeJson().jsonObject
        return Bookings(parseSlots(json))
    }

    private fun parseSlots(json: JsonObject): List<Int> {
        val slotsJson = json["slots"] ?: return emptyList()
        return try {
            slotsJson.jsonArray.content.map { it.int }
        } catch (e: Exception) {
            listOf(slotsJson.int)
        }
    }
}

@ImplicitReflectionSerializer
fun main() {
    val json = """{"slots": 1}"""
    val result = Json.parse<Bookings>(json)
    println(result) // prints Bookings(slots=[1])
}

Upvotes: 5

Related Questions