5

Lets say there is abstract class BaseClass ...

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable(with = PolymorphicSerializer::class)
abstract class BaseClass {
    @SerialName("bid")
    var baseId: String? = null
 }

... class ExtendedClass that extends it ...

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
class ExtendedClass
    (@SerialName("eid")
     var newId: String? = null
) : BaseClass()

... and PolymorphicSerializer

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement

object PolymorphicSerializer  : JsonContentPolymorphicSerializer<BaseClass>(BaseClass::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out BaseClass> {
        return ExtendedClass.serializer()
    }
}

PolymorphicSerializer is used because JSON can be deserialized as different classes that extend BaseClass based on some property (for sake of simplicity I just used ExtendedClass.serializer() )

Now, if we try to deserialize generic JSON to ExtendedClass ...

class DeserializationTest {
    @Test
    fun deserialization_isCorrect() {
        val extendedClass = Json {
            ignoreUnknownKeys = true
        }.decodeFromString<BaseClass>("""{"bid":"bid","eid":"eid"}""")

        assertEquals("bid", extendedClass.baseId)
    }
}

... test fails

java.lang.AssertionError: 
Expected :bid
Actual   :null

If I try to serialize ExtendedClass ...

val extendedClass = ExtendedClass()
extendedClass.baseId = "bid"
extendedClass.newId = "eid"

val encodedJson = Json {
    ignoreUnknownKeys = true
}.encodeToString(extendedClass)

I can see encodedJson is {"eid":"eid"} but if I remove JsonContentPolymorphicSerializer this works fine (replace @Serializable(with = PolymorphicSerializer::class) with @Serializable ), encodedJson is {"bid":"bid","eid":"eid"}

So, for some reason JsonContentPolymorphicSerializer does not take into account properties from super class.

Anyone knows why is that so?

BZKN
  • 1,499
  • 2
  • 10
  • 25
Ivan Škugor
  • 558
  • 1
  • 6
  • 21
  • Did you find solution? Im thinking of including two properties with the same value, one acting as the selecer - as it's seemingly removed – MikkelT Nov 29 '22 at 17:18
  • @MikkelT there is an issue on Github and a workaround is mentioned there: https://github.com/Kotlin/kotlinx.serialization/issues/1433 Unfortunately, still no nice solution for the problem. – Ivan Škugor Dec 12 '22 at 12:15

1 Answers1

-1

You are overriding the generated serializer for BaseClass by providing your custom PolymorphicSerializer:

@Serializable(with = PolymorphicSerializer::class)
abstract class BaseClass { ... }

You have effectively erased the serializer for BaseClass. (In fact, I'm surprised this doesn't throw some type of error/failing deserialization.)

Instead, remove @Serializable, and wherever you need to deserialize JSON which does not contain a class discriminator, pass your custom PolymorphicSerializer as the parameter to decodeFromString.

P.s. I would also recommend to name it differently. PolymorphicSerializer is an actual serializer in kotlinx.serialization which threw me off. ;)

Steven Jeuris
  • 18,274
  • 9
  • 70
  • 161
  • Hi Steven. Sorry about naming, it was just dummy name, unfortunately a bad one. Regarding your solution, I just tried it, but it does not work, baseId is still null after deserialization. – Ivan Škugor May 24 '21 at 15:29
  • This doesn't work for me either. Moreover, sometimes you need to declare the custom `JsonContentPolymorphicSerializer` in the base class, for example when you need it to serialize a polymorphic `List< BaseClass >` (from the example above). AFAIK there is no way to implement a custom List serializer that works with polymorphic collections. – fast3r May 17 '22 at 15:19
  • @fast3r Then you need to create a `ListSerializer` and pass your custom serializer as the parameter. – Steven Jeuris May 17 '22 at 16:50