SerializationException: Class 'ArrayList' is not registered for polymorphic serialization in the scope of 'Any'.
See original GitHub issueDescribe the bug
Following code work on serialization pre-release artifact org.jetbrains.kotlinx:kotlinx-serialization-runtime
.
When using same code with 1.0.0-RC2
or later with artifact org.jetbrains.kotlinx:kotlinx-serialization-json
I get runtime exception
SerializationException: Class 'ArrayList' is not registered for polymorphic serialization in the scope of 'Any'.
I’ve tried to pull out the essential parts from a larger project.
#1252 may be related. I’m not sure if this is a duplicate of different use case, or a separate issue.
To Reproduce
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
interface RpcSerializer<ReqT, ResT> {
fun serializeRequest(rpcRequest: RpcRequest): ReqT
fun serializeResponse(rpcResponse: RpcResponse): ResT
fun deserializeRequest(request: ReqT): RpcRequest
fun deserializeResponse(response: ResT): RpcResponse
}
class JsonRpcSerializer(serializerModule: SerializersModule) : RpcSerializer<String, String> {
private val serializer = Json {
useArrayPolymorphism = true
encodeDefaults = true
serializersModule = serializerModule
}
override fun serializeRequest(rpcRequest: RpcRequest): String {
return serializer.encodeToString(rpcRequest)
}
override fun serializeResponse(rpcResponse: RpcResponse): String {
return serializer.encodeToString(rpcResponse)
}
override fun deserializeRequest(request: String): RpcRequest {
return serializer.decodeFromString(request)
}
override fun deserializeResponse(response: String): RpcResponse {
return serializer.decodeFromString(response)
}
}
class RpcSerializerImpl constructor(
private val actualSerializer: RpcSerializer<String, String> = JsonRpcSerializer(
module
)
) : RpcSerializer<String, String> {
override fun serializeRequest(rpcRequest: RpcRequest) = actualSerializer.serializeRequest(rpcRequest)
override fun serializeResponse(rpcResponse: RpcResponse) = actualSerializer.serializeResponse(rpcResponse)
override fun deserializeRequest(request: String) = actualSerializer.deserializeRequest(request)
override fun deserializeResponse(response: String) = actualSerializer.deserializeResponse(response)
}
@Serializable
data class Identifier(val name: String, val description: String)
@Serializable
data class Point(val x: Double, val y: Double, val id: Identifier)
@Serializable
data class RpcIdentifier(
val serviceName: String,
val name: String,
val parameters: List<String>,
val returnType: String
)
@Serializable
data class RpcRequest(
val id: Long,
val functionSignature: RpcIdentifier,
val arguments: List<RpcData>
)
@Serializable
class RpcSynchronousResponse(
override val id: Long,
@Polymorphic
val data: RpcData
): RpcResponse() {
override fun toString(): String {
return "RpcSynchronousResponse[$id]($data)"
}
}
val module = SerializersModule {
polymorphic(Any::class) {
subclass(RpcSynchronousResponse::class)
subclass(RpcData::class)
subclass(Point::class)
}
polymorphic(RpcData::class) {
subclass(RpcData::class)
}
polymorphic(RpcResponse::class) {
subclass(RpcSynchronousResponse::class)
}
}
val format = Json { serializersModule = module }
@Serializable
data class RpcData(
@Polymorphic
val value: Any?
)
@Serializable
abstract class RpcResponse {
abstract val id: Long
}
@Serializable
class RpcExceptionResponse(
override val id: Long,
val exception: String
): RpcResponse() {
override fun toString(): String {
return "RpcExceptionResponse[$id]($exception)"
}
}
fun main() {
val serializer = RpcSerializerImpl()
val point1 = Point(1.0,1.0,Identifier("one","111"))
val point2 = Point(2.0,-1.0,Identifier("two","222"))
val data = RpcData(listOf(point1,point2))
val responseToSerialize = RpcSynchronousResponse(42L, data)
println("responseToSerialize: $responseToSerialize")
val jsonString = serializer.serializeResponse(responseToSerialize)
println("jsonString $jsonString")
val backAgain = serializer.deserializeResponse(jsonString)
println("backAgain: $backAgain")
}
Expected behavior
Same result as when using pre-release org.jetbrains.kotlinx:kotlinx-serialization-runtime
version 1.0-M1-1.4.0-rc
, printout as follows:
responseToSerialize: RpcSynchronousResponse[42](RpcData(value=[Point(x=1.0, y=1.0, id=Identifier(name=one, description=111)), Point(x=2.0, y=-1.0, id=Identifier(name=two, description=222))])) jsonString [“RpcSynchronousResponse”,{“id”:42,“data”:[“RpcData”,{“value”:[“kotlin.collections.ArrayList”,[[“Point”,{“x”:1.0,“y”:1.0,“id”:{“name”:“one”,“description”:“111”}}],[“Point”,{“x”:2.0,“y”:-1.0,“id”:{“name”:“two”,“description”:“222”}}]]]}]}] backAgain: RpcSynchronousResponse[42](RpcData(value=[Point(x=1.0, y=1.0, id=Identifier(name=one, description=111)), Point(x=2.0, y=-1.0, id=Identifier(name=two, description=222))]))
Environment
- Kotlin version: 1.4.30
- Library version: 1.1.0-RC
- Kotlin platforms: JVM,
- Gradle version: 6.6.1
- IDE version (if bug is related to the IDE) [e.g. IntellijIDEA 2020.3.2
- Other relevant context [e.g. OS version, JRE version, … ] Java 1.8
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:9 (2 by maintainers)
Top GitHub Comments
@jannehof this worked for me by adding a simple cast fun. My code:
subClass(ArrayList::class, ListSerializer(PolymorphicSerializer(Any::class)).cast())
the extension fun:@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") private inline fun <T> KSerializer<List<Any>>.cast(): KSerializer<T> { return this as KSerializer<T> }
My use case: I needed to serializer a list which was either a string or a customDto. If u you need any more info for my code let me know. @sandwwraith i think it’s ok to close this issue as your example works.
Im guessing you are using this factory fun:
Try to use this fun from stdlib which is not returning singletonList :