com.fasterxml.jackson.databind.exc.MismatchedInputException: Kotlin sealed class Json Mapping

225 views Asked by At

Using Spring Boot with Kotlin, Deserialization of this sealed class works for Range type but fails for Exactly type. The serialization works fine but the deserialization doesn't work.

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.WRAPPER_OBJECT,
    property = "type"
)
sealed class NumberRange {
    @JsonIgnore
    abstract fun isEmpty(): Boolean

    @JsonIgnore
    abstract fun rangeSize(): UInt

    @get:JsonIgnore
    abstract val first: UInt

    @JsonTypeName("Exactly")
    data class Exactly(val size: UInt) : NumberRange() {
        override fun isEmpty(): Boolean = size == 0u
        override fun rangeSize(): UInt = if (isEmpty()) 0u else 1u
        override val first: UInt = size
    }

    @JsonTypeName("Range")
    data class Range(val start: UInt, val end: UInt) : NumberRange() {
        override fun isEmpty(): Boolean = end > start
        override fun rangeSize(): UInt = if (isEmpty()) 0u else end - start + 1u
        override val first: UInt = start
    }
}

Error

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.optiwatch.apis.optimizations.entities.NumberRange$Exactly` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

FYI The weirdest part is that is I just add another member to Exactly class, it just works. This is confusing.

data class Exactly(val size: UInt, val ignore: UInt = 0u) : NumberRange()

1

There are 1 answers

0
Ice On

The Exactly class does not have a default constructor. The only way to create an instance of the Exactly class is to use the constructor that takes a UInt as a parameter.

The reason why the error goes away when you add another member to the Exactly class is because Jackson now has enough information to create an instance of the class. The additional member, ignore, is not used by Jackson, but it is enough to satisfy the requirement for a default constructor.

The solution to this problem is to add a default constructor to the Exactly class. This can be done as follows:

data class Exactly(val size: UInt = 0u) : NumberRange()