'Why am I getting a JsonDecodingException exception in kotlinx.serialization?

I'm trying to create a custom deserializer using kotlinx.serialization but when I deerialize a JSON payload with null parameters, an exception kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset 31: Expected string or non-null literal. is threw. I apologize if there is any English error; if more information is missing, just let me know that I will edit the post.

Note: There is nothing wrong with the EventDecoder.

JSON:

{"t":null,"s":null,"op":11,"d":null}

Exception:

16:56:45.012 [main] ERROR io.github.discordkt.common.gateway.Gateway - Unexpected JSON token at offset 31: Expected string or non-null literal.
 JSON input: {"t":null,"s":null,"op":11,"d":null}
kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset 31: Expected string or non-null literal.
 JSON input: {"t":null,"s":null,"op":11,"d":null}
    at kotlinx.serialization.json.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:26)
    at kotlinx.serialization.json.internal.JsonReader.fail(JsonReader.kt:311)
    at kotlinx.serialization.json.internal.JsonReader.takeString(JsonReader.kt:142)
    at kotlinx.serialization.json.internal.StreamingJsonInput.decodeString(StreamingJsonInput.kt:179)
    at kotlinx.serialization.json.internal.StreamingJsonInput.decodeObjectIndex(StreamingJsonInput.kt:115)
    at kotlinx.serialization.json.internal.StreamingJsonInput.decodeElementIndex(StreamingJsonInput.kt:88)
    at io.github.discordkt.core.events.EventDeserializer.deserialize(EventDeserializer.kt:56)
    at io.github.discordkt.core.events.EventDeserializer.deserialize(EventDeserializer.kt:29)
    at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:52)
    at kotlinx.serialization.json.internal.StreamingJsonInput.decodeSerializableValue(StreamingJsonInput.kt:33)
    at kotlinx.serialization.DecodingKt.decode(Decoding.kt:521)
    at kotlinx.serialization.json.Json.parse(Json.kt:131)
    at io.github.discordkt.core.gateway.ws.DiscordGatewayListener.onMessageReceived(DiscordGatewayListener.kt:54)
    at io.github.discordkt.core.gateway.ws.DiscordGatewayAdapter$setupSession$$inlined$collect$1.emit(Collect.kt:136)
    at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:58)
    at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Channels.kt)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:84)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at OiKt.main(Oi.kt:14)
    at OiKt.main(Oi.kt)

Code:

object EventDeserializer: DeserializationStrategy<Event?> {

    override val descriptor = SerialDescriptor("Event") {

        /** The `op` field represents the [GatewayCode] that is used to identify the event type. */
        element("op", GatewayCode.descriptor, isOptional = false) // Index 0

        /** The `t` field represents the gateway event name. */
        element("t", nullable<String>().descriptor, isOptional = true) // Index 1

        /** The `s` field represents the sequence number that is used to resume the connection. */
        element("s", nullable<Int>().descriptor, isOptional = true) // Index 2

        /** The `d` field represents a [JsonObject] that includes the event data. */
        element("d", nullable<JsonObject>().descriptor, isOptional = false) //Index 3

    }

    override fun deserialize(decoder: Decoder): Event? {

        var code: GatewayCode = GatewayCode.UNKNOWN
        var eventName: String? = null
        var sequence: Int? = null
        var event: Event? = null

        with(decoder.beginStructure(descriptor)) {
            loop@ while(true) {
                when(val index = decodeElementIndex(descriptor)) {
                    CompositeDecoder.READ_DONE -> break@loop
                    0 -> code = decodeSerializableElement(descriptor, index, GatewayCode).also(::println)
                    1 -> eventName = decodeNullableSerializableElement(descriptor, index, nullable<String?>())
                    2 -> sequence = decodeNullableSerializableElement(descriptor, index, nullable<Int?>())
                    3 -> event = when(code) {
                        GatewayCode.DISPATCH -> EventDecoder(this, descriptor, eventName, index, sequence)
                        GatewayCode.INVALID_SESSION -> EventDecoder.INVALID_SESSION(this, descriptor, index)
                        GatewayCode.HANDSHAKE -> EventDecoder.HANDSHAKE_MESSAGE(this, descriptor, index)
                        GatewayCode.HEARTBEAT_ACK -> HeartbeatAckEvent
                        GatewayCode.HEARTBEAT -> HeartbeatOrderEvent
                        GatewayCode.RECONNECT -> ReconnectOrderEvent
                        else -> null
                    }
                }
            }

            endStructure(descriptor)
            return event
        }

    }

    override fun patch(decoder: Decoder, old: Event?): Event? {
        throw UpdateNotSupportedException(descriptor.serialName)
    }

    @OptIn(ExperimentalStdlibApi::class)
    @Suppress("UNCHECKED_CAST")
    private inline fun <reified T> nullable(): KSerializer<T?> {
        return serializer(typeOf<T?>()) as KSerializer<T?>
    }

}

JSON configuration:

@OptIn(UnstableDefault::class)
val DiscordJsonParser = Json(
    JsonConfiguration(ignoreUnknownKeys = true, isLenient = true)
)


Solution 1:[1]

In my case it was like this :

I was trying to deserialize a data to a non-primitive type (which was a data class) and the problem was that the serialized data, was not an instance of that data class.

I had serialized a String type but I was deserializing it to another type.

Solution 2:[2]

In my case the problem was using @SerializedName on some of fields inside the model. Because the name used inside the annotation was different than the field name I was getting this exception.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mahdi nezam parast
Solution 2 Reza