Search code examples
jsonkotlinserializationkotlinx.serialization

Kotlin Polymorphic deserialization not deserializing values


I'm trying to deserialize JSON into different sealed subclasses, the class mappings work, but the actual values are all null

Example1:

{
  "eventName": "SUCCESS",
  "productName": "BLAH"
}

Example2:

{
  "eventName": "FAILURE",
  "productName": "BLAH",
  "error": "Something went wrong"
}

The base sealed class looks like this:

@ExperimentalSerializationApi
@Serializable(with = EventSerializer::class)
sealed class Event {
    val eventName: String? = null
    val productName: String? = null
}

I have three subclasses

@Serializable
class EventFailure : Event()
@Serializable
class EventSuccess : Event()
@Serializable
class EventInvalid : Event()

and this is the Serializer

@ExperimentalSerializationApi
@Serializer(forClass = Event::class)
object EventSerializer : JsonContentPolymorphicSerializer<Event>(Event::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Event> {
        return element.jsonObject["eventName"]?.jsonPrimitive?.content?.let {
            when (EventType.valueOf(it)) {
                EventType.FAILURE -> EventFailure.serializer()
                EventType.SUCCESS -> EventSuccess.serializer()
            }
        } ?: EventInvalid.serializer()
    }
}

When I deserialize a JSON list, all the values end up null:

val events = JSON.decodeFromString<Array<Event>>(it.body())

events.forEach {
    println(it)
    println(it.productName)
}
com.blah.EventSuccess@2ca132ad
null
com.blah.EventFailure@1686f0b4
null

If I change Event from a sealed class to a normal class without the custom serializer, data correctly deserializes into the Even class filling in all the values.

Any suggestions on how to make the deserialization into EventSuccess / EventFailure work correctly?


Solution

  • I made a custom JSON class with custom classDiscriminator

        companion object {
            val JSON = Json {
                prettyPrint = true
                ignoreUnknownKeys = true
                encodeDefaults = true
                classDiscriminator = "eventName"
            }
        }
    
    @Serializable
    @SerialName("SUCCESS")
    class EventSuccess : Event()
    @Serializable
    @SerialName("FAILURE")
    class EventFailure : Event()
    

    and I removed the custom serializer

    //@Serializable(with = EventSerializer::class)
    @Serializable
    sealed class Event {
    

    and it's working correctly now.

    I'm keeping the question open to see if there are perhaps ways of fixing the custom serializer implementation.