jackson custom deserializer for sealed class

67 Views Asked by At

I was writing the custom deserializer for sealed class using jackson

my sealed class looks like this :

sealed class ParentClass {
    class class1() : ParentClass()
    class class2() : ParentClass()
}

my deserializer looks like this :

class ParentClassDeserializer : JsonDeserializer<ParentClass>() {
  override fun deserialize(p: JsonParser, ctxt: DeserializationContext): ParentClass {
mapper.readTree(node, ParentClass)

now when i used it like this

@JsonDeserialize(using = ParentClassDeserializer::class)
sealed class ParentClass {

so in custom deserializer when at the end i was using mapper to convert node to a class the deserializer is again gets invoked and goes into recursion which caused StackOverFlow Exception

Wanted to know if there is a way to do it for sealed classes in kotlin? I tried different mappers config but all of them were using jackson internally which is causing the recursive invocation of deserializer.

1

There are 1 best solutions below

2
On

I don't know if you have a specific reason for using a custom (de)serializer, but technically you could use polymorphic deserialization. In order for that to work, though, Jackson needs to be able to distinguish between the various child classes.

Jackson has a few built-in strategies to try and do that. For example, you can add a property to the serialized value with a unique value, or you can even let Jackson try to figure it out based on the available properties. In your example, however, all child classes are the same, so even if you added the relevant annotations for polymorphic deserialization, Jackson wouldn't be able to figure out which one it should deserialize to.

Even if you decide to write the deserializer yourself, you'd need a way to differentiate which type you want to deserialize to.

If you want to use polymorphic deserialization, you can check the following code snippet:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
sealed class Parent {
    @JsonTypeName("child1")
    class Child1 : Parent()

    @JsonTypeName("child2")
    class Child2 : Parent()
}

fun main() {
    val raw = """{"type":"child1"}"""
    val objectMapper = jacksonObjectMapper()
    val deserialized = objectMapper.readValue<Parent>(raw)
    println(deserialized::class) // prints "class Parent$Child1"
}