Skip to content

"Serializer for subclass not found" for nested sealed class with custom serializer #2625

Closed as not planned
@PatrickHum-at

Description

@PatrickHum-at

Describe the bug

I'm running into an issue where I need to use a JsonContentPolymorphicSerializer on a nested sealed class. The children of the nested sealed class share the same top level discriminator, and are differentiated by their content.

To Reproduce

@Serializable
sealed class Base {
    @Serializable
    @SerialName("a")
    data class A(val value: Int) : Base()

    @Serializable(with = InnerBase.Companion::class)
    @SerialName("b")
    sealed class InnerBase : Base() {
        @Serializable
        data class D(val value: Int) : InnerBase()

        @Serializable
        data class E(val value: Int) : InnerBase()

        companion object : JsonContentPolymorphicSerializer<InnerBase>(InnerBase::class) {
            override fun selectDeserializer(element: JsonElement): DeserializationStrategy<InnerBase> {
                return if ("foo" in element.jsonObject) {
                    D.serializer()
                } else {
                    E.serializer()
                }
            }
        }
    }
}

class NestedCustomSerializerTest {
    val json = Json { }

    @Test
    fun `test deserialize D`() {
        val actual = json.decodeFromString<Base>(
            """{
            "type": "b",
            "foo": 1,
            "value": 2
            }""".trimMargin()
        )
        assertEquals(Base.InnerBase.D(2), actual)
    }

    @Test
    fun `test deserialize A`() {
        val actual = json.decodeFromString<Base>(
            """{
            "type": "a",
            "value": 7
            }""".trimMargin()
        )
        assertEquals(Base.A(7), actual)
    }
}

When running test deserialize D I get the following error:

kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 0: Serializer for subclass 'b' is not found in the polymorphic scope of 'Base' at path: $
Check if class with serial name 'b' exists and serializer is registered in a corresponding SerializersModule.
To be registered automatically, class 'b' has to be '@Serializable', and the base class 'Base' has to be sealed and '@Serializable'.
JSON input: {
            "type": "b",
            "foo": 1,
            "value": 2
            }
	at app//kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
	at app//kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
	at app//kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:598)
	at app//kotlinx.serialization.json.internal.AbstractJsonLexer.fail$default(AbstractJsonLexer.kt:596)
	at app//kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:85)
	at app//kotlinx.serialization.json.Json.decodeFromString(Json.kt:107)

Expected behavior

The JSON is deserialized to Base.InnerBase.D(2).

Environment

  • Kotlin version: 1.9.23
  • Library version: 1.6.3
  • Kotlin platforms: JVM (Android)
  • Gradle version: 8.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions