Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serializer<T>() does not work on Kotlin/JS when T is an interface #1394

Closed
mtorials opened this issue Apr 1, 2021 · 2 comments
Closed

serializer<T>() does not work on Kotlin/JS when T is an interface #1394

mtorials opened this issue Apr 1, 2021 · 2 comments
Assignees

Comments

@mtorials
Copy link

mtorials commented Apr 1, 2021

Describe the issue

I have a basic polymorphic structure with a base interface Event and a subclass MessageEvent.
When I try to deserialize a json object (type MessageEvent) with a type discriminator as the base class (here Event), I get an error Serializer for class 'Event' is not found.. This is however expected, because I am targeting Kotlin/JS and Event is an interface (#1000).

But serializing and deserializing another class (Wrapper) with an attribute of type Event works fine. The json object gets a type discriminator and can be deserialized. Why is it possible to get the serializer for Event in this case?

To get a type discriminator and successfully serialize/deserialize a MessageEvent directly it is possible to specify the PolymorphicSerializer explicitly when calling encodeToString/decodeFromString like suggested in issue #1000. But this does not work with Lists and Arrays.

When serializing Lists and Arrays you can not explicitly specify the PolymorphicSerializer for the generic type, so there is no type discriminator in the json. Trying to deserialize fails with Serializer for class 'Event' is not found.. Is there a workaround for this problem?

To Reproduce

interface Event {
    val id: String
}

@Serializable
data class MessageEvent(
    override val id: String,
    val message: String,
) : Event

@Serializable
data class Wrapper(
    val event: Event
)

val format = Json {
    classDiscriminator = "type"
    prettyPrint = true
    ignoreUnknownKeys = true
    serializersModule = SerializersModule {
        polymorphic(Event::class) {
            subclass(MessageEvent::class)
        }
    }
}

// This works fine, but why?
val wrapperIn = Wrapper(MessageEvent("id3", "A wrapped message"))
val wrapperString = format.encodeToString(wrapperIn)
val wrapper: Wrapper = format.decodeFromString(wrapperString)
println(wrapper) // Wrapper(event=MessageEvent(id=id3, message=A wrapped message))

// Also works fine
val messageEventIn: Event = MessageEvent("id1", "This is a message")
val messageEventString = format.encodeToString(PolymorphicSerializer(Event::class), messageEventIn)
val event: Event = format.decodeFromString(PolymorphicSerializer(Event::class), messageEventString)
println(event) // MessageEvent(id=id1, message=This is a message)

// This does not work
val messageEventListIn = listOf(MessageEvent("id4", "hi"), MessageEvent("id4", "Hui"))
val messageEventListString = format.encodeToString(messageEventListIn)
// Json looks like this (no type discriminator)
//[
//    {
//        "id": "id4",
//        "message": "hi"
//    },
//    {
//        "id": "id4",
//        "message": "Hui"
//    }
//]
val messageEventList : List<Event> = format.decodeFromString(messageEventListString) // errors with Serializer for class 'Event' is not found
println(messageEventList)

Environment

  • Kotlin version: 1.4.31
  • Library version: 1.1.0
  • Kotlin platforms: JS
  • Gradle version: 6.6.1
@sandwwraith
Copy link
Member

As a workaround, you can use encodeToString(ListSerializer(PolymorphicSerializer(Event::class)), ...)

Regarding the issue: inline serializer<T>() (which is used in decodeToString) does not return correct polymorphic serializer on JS now, it's an issue. See also #1116 and #1078

@sandwwraith sandwwraith self-assigned this Apr 12, 2021
@sandwwraith sandwwraith changed the title Kotlin/JS Polymorphic (de-)serialization with Lists/Arrays. serializer<T>() does not work on Kotlin/JS when T is an interface Apr 20, 2021
@sandwwraith
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants