question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SerializationException: Class 'ArrayList' is not registered for polymorphic serialization in the scope of 'Any'.

See original GitHub issue

Describe 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:open
  • Created 3 years ago
  • Reactions:1
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
GeorgePap-719commented, Apr 11, 2022

@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.

0reactions
GeorgePap-719commented, Dec 19, 2022

Im guessing you are using this factory fun:

public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)

Try to use this fun from stdlib which is not returning singletonList :

public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
Read more comments on GitHub >

github_iconTop Results From Across the Web

Class 'ArrayList' is not registered for polymorphic serialization ...
SerializationException : Class 'ArrayList' is not registered for polymorphic serialization in the scope of 'Any'. Relates to 1.
Read more >
Kotlin Serialization issues: Class is not registered for ...
SerializationException : Class 'CustomConvertible' is not registered for polymorphic serialization in the scope of 'Convertible'.
Read more >
How to serialize an array of different types of value ... - Reddit
SerializationException : Class 'String' is not registered for polymorphic serialization in the scope of 'Serializable'.
Read more >
Polymorphic serialization doesn't work for deeper sealed class ...
I have the following code: @Serializable sealed class Component ... is not registered for polymorphic serialization in the scope of ...
Read more >
PolymorphicModuleBuilder - GitHub Pages
Polymorphic ModuleBuilder ... A builder which registers all its content for polymorphic serialization in the scope of the base class. If baseSerializer is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found