I have a data class that inherits from a sealed class that I want to serialize into a java.util.ConcurrentLinkedQueue.
@Serializable
sealed class A
@Serializable
data class B : A {
val x: String,
val y: Int,
val z: Boolean
}
@Serializable
data class C : A {
val a: String,
val b: Int,
val c: Boolean
}
@Serializable
data class MyServiceCache(
val someOtherProp = "Hello World",
@Serializable(with = ConcurrentLinkedQueueSerializer::class)
val networkCallQueue: ConcurrentLinkedQueue<A> = ConcurrentLinkedQueue(),
)
I also have a custom serializer
class ConcurrentLinkedQueueSerializer<T>(
private val dataSerializer: KSerializer<List<T>>
) : KSerializer<ConcurrentLinkedQueue<T>> {
override val descriptor: SerialDescriptor = dataSerializer.descriptor
override fun serialize(encoder: Encoder, value: ConcurrentLinkedQueue<T>) = dataSerializer.serialize(
encoder,
value.toList()
)
override fun deserialize(decoder: Decoder) = ConcurrentLinkedQueue(dataSerializer.deserialize(decoder))
}
This is all good at compile time. When I attempt to serialize in my application at runtime, I get the following error:
Caused by: java.lang.IllegalArgumentException: Polymorphic value has not been read for class null
at kotlinx.serialization.internal.AbstractPolymorphicSerializer.deserialize(AbstractPolymorphicSerializer.kt:67)
at com.my.app.service.network.ConcurrentLinkedQueueSerializer.deserialize(MyServiceCache.kt:35)
at com.my.app.service.network.ConcurrentLinkedQueueSerializer.deserialize(MyServiceCache.kt:27)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:168)
at com.my.app.service.network.MyServiceCache$$serializer.deserialize(MyServiceCache.kt:16)
at com.my.app.service.network.MyServiceCache$$serializer.deserialize(MyServiceCache.kt:16)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
at kotlinx.serialization.json.Json.decodeFromString(Json.kt:107)
What's wrong with my custom serializer? I tried to follow the instructions in the documentation as best I could. https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#custom-serializers-for-a-generic-type
The guidance you linked says this:
What this means is that, when the framework instantiates a serializer for a generic class (say
MyClass<T>) (with that serializer implementingKSerializer<MyClass<T>>) it passes an object of typeKSerializer<T>to it. It does this so that serializer has the tool it needs to serialize whatever generic object(s) it contains.You instead have put your surrogate serializer as the constructor parameter.
To follow the instructions, you need to rewrite with the serializer for the generic type
Tas constructor parameter. This will be provided by the framework at runtime:The other thing done here is we have had to instantiate your surrogate serializer
surrogateListSerializerby hand (using the built-inListSerializer) using the provided generic serializer as parameter to do so (following the same above rule for serializers of generic classes).