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.

@Polymorphic annotation does not work for properties when used on type

See original GitHub issue

Describe the bug

I have a class hierarchy with several inheritance hierarchies. In this hierarchy, members marked as Polymorphic do not work.

To Reproduce

Here is a test:

import kotlinx.serialization.Polymorphic
import kotlinx.serialization.PolymorphicSerializer
import kotlin.test.Test
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import kotlin.test.assertEquals

@Serializable
sealed class Fruit

@Serializable
data class Banana(val name: String) : Fruit()
@Serializable
data class Apple(val id: Int) : Fruit()

@Serializable
sealed class Box

@Serializable
data class SmallBox(val content: @Polymorphic Fruit): Box()

@Serializable
data class LargeBox(val content: List<@Polymorphic Fruit>): Box()

class Test {
    @Test
    fun test() {
        val format = Json(context= SerializersModule { 
            polymorphic(Fruit::class) { 
                Banana::class with Banana.serializer()
                Apple::class with Apple.serializer()
            }
            polymorphic(Box::class) { 
                SmallBox::class with SmallBox.serializer()
                LargeBox::class with LargeBox.serializer()
            }
        })

        val data = SmallBox(Banana("Test"))
//        val data = LargeBox(listOf(Banana("Test"), Apple(22)))

        val asString = format.stringify(PolymorphicSerializer(Box::class), data)
        println(asString)
        val obj = format.parse(PolymorphicSerializer(Box::class), asString)
        assertEquals(data, obj)
    }
}

The printout is {"type":"SmallBox","content":{}} And the test fails with

java.lang.InstantiationError: Fruit
	at Fruit$$serializer.deserialize(BugTest.kt)
	at Fruit$$serializer.deserialize(BugTest.kt:10)
	at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:33)
	at kotlinx.serialization.json.internal.AbstractJsonTreeInput.decodeSerializableValue(TreeJsonInput.kt:48)
	at kotlinx.serialization.TaggedDecoder$decodeSerializableElement$1.invoke(Tagged.kt:257)
	at kotlinx.serialization.TaggedDecoder.tagBlock(Tagged.kt:270)
	at kotlinx.serialization.TaggedDecoder.decodeSerializableElement(Tagged.kt:257)
	at SmallBox$$serializer.deserialize(BugTest.kt)
	at SmallBox$$serializer.deserialize(BugTest.kt:21)
	at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:33)
	at kotlinx.serialization.json.internal.AbstractJsonTreeInput.decodeSerializableValue(TreeJsonInput.kt:48)
	at kotlinx.serialization.CoreKt.decode(Core.kt:79)
	at kotlinx.serialization.json.internal.TreeJsonInputKt.readJson(TreeJsonInput.kt:27)
	at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:41)
	at kotlinx.serialization.json.internal.StreamingJsonInput.decodeSerializableValue(StreamingJsonInput.kt:29)
	at kotlinx.serialization.CoreKt.decode(Core.kt:79)
	at kotlinx.serialization.json.Json.parse(Json.kt:148)
	at BugTest.test(BugTest.kt:45)

When I use the line with LargeBox instead of the SmallBox line, so, polymorphic elements in a list, it works as expected.

Expected behavior The printout should be {"type":"SmallBox","content":{"type":"Banana","name":"Test"}}

Environment

  • Kotlin version: 1.3.30
  • Library version: 0.11.0
  • Kotlin platforms: JVM, I did not try other platforms
  • Gradle version: 5.2.1
  • JVM: 1.8.0_201 (Oracle Corporation 25.201-b09)
  • OS: Linux 4.20.6-200.fc29.x86_64 amd64

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
sandwwraithcommented, May 16, 2019

@danielebart Yes, registering is now necessary, since polymorphic serialization is now multiplatform and JS and Native lack a necessary amount of reflection (like Class.forName) to retrieve serializers without registering.

Regarding sealed classes, we have plans to improve their polymorphic serialization (since all their inheritors indeed are known in compile-time and therefore can be registered automatically)

2reactions
danielebartcommented, May 16, 2019

@sandwwraith is it really necessary to define a SerialModule for polymorphic classes? i have this class hierarchy:

@Polymorphic
sealed class A {

    @Serializable
    data class B(val foo: String): A()

    @Serializable
    data class C(val foo: String): A()
}

this is not working without registering a SerialModule (class B is not registered for polymorphic serialization in the scope of class A), am i missing something? i seem to remember that in the previous release this step wasn’t necessary

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why does Jackson polymorphic serialization not work in lists?
(or "@class" or whatever your type field is named) Jackson will serialize it regardless of where the object is located.
Read more >
Polymorphic resolution without annotations, looking for ...
Hey there, I am currently trying to handle polymorphic resolution without using the annotations and unfortunately, I haven't found any completely working ......
Read more >
JsonUnwrapped (Jackson-annotations 2.13.0 API) - FasterXML
Annotation can only be added to properties, and not classes, as it is contextual. When values are deserialized "wrapping" is applied so that...
Read more >
Inheritance in Jackson | Baeldung
This tutorial will demonstrate how to handle inclusion of subtype metadata and ignoring properties inherited from superclasses with Jackson.
Read more >
Inheritance and Polymorphism - Swagger
It is recommended to avoid using conflicting properties (like properties that have the same names, but different data types).
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