Skip to content

2.0.20 with generatedSerializer and reference to interface: SealedClassSerializer init: subclassSerializers contains null #2759

@hfhbd

Description

@hfhbd

Describe the bug

java.lang.NullPointerException
	at kotlinx.serialization.SealedClassSerializer$special$$inlined$groupingBy$1.keyOf(_Collections.kt:1547)
	at kotlinx.serialization.SealedClassSerializer.<init>(SealedSerializer.kt:158)
	at kotlinx.serialization.SealedClassSerializer.<init>(SealedSerializer.kt:97)

This only happens if a child has a reference to the parent interface, in this case SealedClassSerializer.subclassSerializers has nullable entries, but it passes the init check because the array has the same length.

To Reproduce

@Serializable
public sealed interface TestSchema

@Serializable(with = Bar.Companion.CustomSerializer::class)
@SerialName("bar")
@KeepGeneratedSerializer
data class Bar(val bar: Int) : TestSchema {
    companion object {
        internal object CustomSerializer : KSerializer<Bar> by generatedSerializer() // just a dummy for the test
    }
}

@Serializable(with = ASDF.Companion.CustomSerializer::class)
@SerialName("asdf")
@KeepGeneratedSerializer
data class ASDF(
    val child: TestSchema,
) : TestSchema {
    companion object {
        internal object CustomSerializer : KSerializer<ASDF> by generatedSerializer()
    }
}

@Test
fun internalError() {
  val (schema, schema2) = Json.decodeFromString(ListSerializer(TestSchema.serializer()), """
[
  {
    "type": "bar",
    "bar": 42,
  },
  {
    "type": "asdf",
    "child": {
      "type": "bar",
      "bar": 42
    }
  }
]
""")

  assertTrue(schema is Bar)
  assertEquals(42, schema.bar)

  assertTrue(schema2 is ASDF)
  val child = schema2.child
  assertTrue(child is Bar)
  assertEquals(42, child.bar)
}

// Just nice to add it to your internal tests to not break support for polymorphicDefaultDeserializer
@Test
fun internalErrorWithDefault() {
  val (schema, schema2) = Json {
    serializersModule = SerializersModule {
      polymorphicDefaultDeserializer(TestSchema::class) {
        ASDF.Companion.CustomSerializer
      }
    }
  }.decodeFromString(ListSerializer(TestSchema.serializer()), """
[
  {
    "type": "bar",
    "bar": 42
  },
  {
    "child": {
      "type": "bar",
      "bar": 42
    }
  }
]
""")

  assertTrue(schema is Bar)
  assertEquals(42, schema.bar)

  assertTrue(schema2 is ASDF)
  val child = schema2.child
  assertTrue(child is Bar)
  assertEquals(42, child.bar)
}

Expected behavior
No internal exception

Environment

  • Kotlin version: 2.0.20-RC
  • Library version: 1.7.1
  • Kotlin platforms: JVM
  • Gradle version: 8.9
  • IDE version (if bug is related to the IDE): -
  • Other relevant context: -

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions