0

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?

aSemy
  • 5,485
  • 2
  • 25
  • 51
Jan Vladimir Mostert
  • 12,380
  • 15
  • 80
  • 137
  • You might need to enable [array polymorphism](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/#-917438954%2FProperties%2F-702380584) – aSemy Dec 09 '22 at 17:53
  • I've tried enabling that without success. I did get success using a custom classDiscriminator, but still curious how one can fix the custom serializer – Jan Vladimir Mostert Dec 09 '22 at 18:18
  • `productName` is null because that's the default value set `Event`. There's no way to create an `Event` that doesn't have a null product name (either in regular code, or in Kotlinx Serialization). [Properties must be in the constructor](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#constructor-properties-requirement). – aSemy Dec 09 '22 at 18:24

1 Answers1

0

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.

Jan Vladimir Mostert
  • 12,380
  • 15
  • 80
  • 137