Polymorphic deserialization with kotlinx.serialization in Kotlin/Native
Asked Answered
A

2

9

I want to decode a json string containing a list of objects in a polymorphic class structure using kotlinx.serialization in a Kotlin Multiplatform project, but it works only on JVM, not on Native. Here is a minimum reproducible example:

@Serializable
abstract class Project {
    abstract val name: String
}

@Serializable
@SerialName("BasicProject")
data class BasicProject(override val name: String): Project()

@Serializable
@SerialName("OwnedProject")
data class OwnedProject(override val name: String, val owner: String) : Project()

fun main() {
    val data = Json.decodeFromString<List<Project>>("""
        [
            {"type":"BasicProject","name":"example"},
            {"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"} 
        ]
    """))
}  

This works on JVM but throws the following exception on Native:

kotlinx.serialization.SerializationException: Serializer for class ‘Project’ is not found.
Mark the class as @Serializable or provide the serializer explicitly.
On Kotlin/Native explicitly declared serializer should be used for interfaces and enums without @Serializable annotation.message

This problem has been discussed before in the context of encoding and some workarounds have been suggested, e.g. here, but my problem is decoding. Is there a workaround, or do I simply have to implement my own json parser?

Americium answered 9/3, 2021 at 20:8 Comment(3)
Actually, it's not working even in JVM without explicit specifing serializersModule for Json: val module = SerializersModule { polymorphic(Project::class) { subclass(BasicProject::class); subclass(OwnedProject::class) } }; val data = Json { serializersModule = module }.decodeFromString<List<Project>>(...)Monostome
Will it work in Kotlin/Native if you specify above-mentioned serializersModule and pass serializer explicitly: object ListOfProjectSerializer : KSerializer<List<Project>> by ListSerializer(Project.serializer()); val data = Json { serializersModule = module }.decodeFromString(ListOfProjectSerializer, ...) ?Monostome
Yes that works, thank you so much! If you post it as an answer I will mark it accepted.Americium
R
10

You need to explicitly pass respectful serializer and serializersModule:

object ListOfProjectSerializer : KSerializer<List<Project>> by ListSerializer(Project.serializer())

val module = SerializersModule {
    polymorphic(Project::class) {
        subclass(BasicProject::class)
        subclass(OwnedProject::class)
    }
}

fun main() {
    val data = Json { serializersModule = module }.decodeFromString(
        ListOfProjectSerializer,
        """
        [
            {"type":"BasicProject","name":"example"},
            {"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"} 
        ]
        """
    )
}
Refreshing answered 10/3, 2021 at 11:49 Comment(4)
polymorphic() now requires three arguments, not twoSavick
@DzmitryLazerka This code is still valid with the current latest kotlinx.serialization (1.3.2). polymorphic method has an overload, which is defined as an extension method and used here. It has 3 arguments, but two of them have default values.Monostome
@МихаилНафталь the link does not work. I cant find extension method. EDIT: nvr mind, I had to add import kotlinx.serialization.modules.*Severance
Seems, they've revamped docs; current correct link: kotlin.github.io/kotlinx.serialization/…Monostome
B
0

I had the same problem and found another solution: In version 1.6.3 of kotlinx serialization you can use both the PolymorphicSerializer for encoding and decoding. This requires to setup all polymorphic classes in a SerializerModule.

e.g. interface Request and class BaseRequest: Request

val baseRequest = BaseRequest()

 Json { serializersModule = module }.encodeToString(PolymorphicSerializer(Request::class), baseRequest)

val data = Json { serializersModule = module }.decodeFromString(PolymorphicSerializer(Request::class), encodedJsonData)
Brisance answered 22/2 at 12:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.