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()
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: