-
Notifications
You must be signed in to change notification settings - Fork 660
Closed
Labels
Description
A generated generic serializer containing a property with custom generic serializer gives UnitSerializer
to said custom serializer instead of the correct type argument.
The code below outputs:
kotlinx.serialization.internal.UnitSerializer@6895a785
Exception in thread "main" java.lang.ClassCastException: class com.test.Thing cannot be cast to class kotlin.Unit (com.test.Thing and kotlin.Unit are in unnamed module of loader 'app')
at kotlinx.serialization.internal.UnitSerializer.serialize(Primitives.kt:79)
at kotlinx.serialization.json.internal.StreamingJsonEncoder.encodeSerializableValue(StreamingJsonEncoder.kt:219)
at com.test.BoxSerializer.serialize(brokenTest.kt:30)
at com.test.BoxSerializer.serialize(brokenTest.kt:16)
at kotlinx.serialization.json.internal.StreamingJsonEncoder.encodeSerializableValue(StreamingJsonEncoder.kt:219)
at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableElement(AbstractEncoder.kt:80)
at com.test.Query.write$Self(brokenTest.kt:35)
at com.test.Query$$serializer.serialize(brokenTest.kt:35)
at com.test.Query$$serializer.serialize(brokenTest.kt:35)
at kotlinx.serialization.json.internal.StreamingJsonEncoder.encodeSerializableValue(StreamingJsonEncoder.kt:219)
at kotlinx.serialization.json.Json.encodeToString(Json.kt:85)
at com.test.BrokenTestKt.main(brokenTest.kt:50)
at com.test.BrokenTestKt.main(brokenTest.kt)
To Reproduce
@file:OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
package com.test
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.Json
import java.lang.IllegalStateException
@Serializable(BoxSerializer::class)
sealed class Box<T> {
@Serializable data class BoxA<T>(val value: T): Box<T>()
}
class BoxSerializer<T>(val inner: KSerializer<T>): KSerializer<Box<T>> {
init {
println(inner)
}
override fun deserialize(decoder: Decoder): Box<T> {
return Box.BoxA(decoder.decodeSerializableValue(inner))
}
override val descriptor: SerialDescriptor
get() = SerialDescriptor("Box<${inner.descriptor.serialName}>", inner.descriptor)
override fun serialize(encoder: Encoder, value: Box<T>) {
encoder.encodeSerializableValue(inner, (value as Box.BoxA<T>).value)
}
}
@Serializable
data class Query<T>(
val condition: Box<T>
)
@Serializable
data class Thing(
val x: Int = 0
)
val json = Json
fun main() {
println(json.encodeToString(Query(Box.BoxA(Thing(2)))))
}
Expected behavior
When using a custom serializer, whether or not the class is sealed or abstract should be irrelevant and the argument to the custom serializer should be the correct type, not UnitSerializer
.
The code above would work in this case.
Workaround
Mark your class as open
and protect the constructor.
Environment
- Kotlin version: 1.6.10
- Library version: 1.6.10
- Kotlin platforms: JVM
- Gradle version: 7.1