Kotlinx deserialization does not include properties from abstract class when using "JsonContentPolymorphicSerializer"

Viewed 957

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?

1 Answers

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. ;)

Related