Skip to content

JSON cannot parse polymorphic objects of default type object #2353

@ForteScarlet

Description

@ForteScarlet

Describe the bug

Hello. I have the following code:

@Serializable
sealed class Conf {
    @Serializable
    @SerialName("empty")
    object Empty : Conf() // default

    @Serializable
    @SerialName("simple")
    data class Simple(val value: String) : Conf()
}

fun main() {
    val json = Json {
        isLenient = true
        ignoreUnknownKeys = true
        serializersModule = SerializersModule {
            polymorphicDefaultDeserializer(Conf::class) { Conf.Empty.serializer() }
        }
    }

    // ok
    json.decodeFromString(Conf.serializer(), """{"type": "default"}""").also { println("value: $it") }
    // print value: Conf$Empty@3745e5c6

    // ok
    json.decodeFromString(Conf.serializer(), """{"unknown": "Meow"}""").also { println("value: $it") }
    // print value: Conf$Empty@3745e5c6

    // error: JsonDecodingException: Unexpected JSON token at offset 1: Expected beginning of the string, but got } at path: $ JSON input: {}
    json.decodeFromString(Conf.serializer(), """{}""").also { println("value: $it") }
}

When I type an empty json object {} directly, it does not resolve to an object object based on the default type.

Similarly, when I want Simple to be the default type, the input json must also have arbitrary properties.

@Serializable
sealed class Conf {
    @Serializable
    @SerialName("empty")
    object Empty : Conf() // default

    @Serializable
    @SerialName("simple")
    data class Simple(val value: String = "default value") : Conf()
}

fun main() {
    val json = Json {
        isLenient = true
        ignoreUnknownKeys = true
        serializersModule = SerializersModule {
            polymorphicDefaultDeserializer(Conf::class) { Conf.Simple.serializer() }
        }
    }

    // ok
    json.decodeFromString(Conf.serializer(), """{"type": "simple"}""").also { println("value: $it") }
    // print value: Simple(value=default value)

    // ok
    json.decodeFromString(Conf.serializer(), """{"value": "value"}""").also { println("value: $it") }
    // print value: Simple(value=value)

    // ok
    json.decodeFromString(Conf.serializer(), """{"unknown": "cat"}""").also { println("value: $it") }
    // print value: Simple(value=default value)

    // error: JsonDecodingException: Unexpected JSON token at offset 1: Expected beginning of the string, but got } at path: $ JSON input: {}
    json.decodeFromString(Conf.serializer(), """{}""").also { println("value: $it") }
}

Expected behavior

Get the correct default type result when entering an empty json string

@Serializable
sealed class Conf {
    @Serializable
    @SerialName("empty")
    object Empty : Conf() // default

   // ....
}

fun main() {
    val json = Json {
        // ...
        serializersModule = SerializersModule {
            polymorphicDefaultDeserializer(Conf::class) { Conf.Empty.serializer() }
        }
    }

    json.decodeFromString(Conf.serializer(), """{}""").also { println("value: $it") }
    // 👉 Expected: value: Conf$Empty@3745e5c6
}

Environment

  • Kotlin version: 1.8.21
  • Library version: 1.5.1
  • Kotlin platforms: JVM
  • Gradle version: 7.6

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions