jackson-module-kotlin
jackson-module-kotlin copied to clipboard
Kotlin Serialization of Polymorphic Interfaces Erases Parent Type
When working with a fairly complex api that we have, we need to be able to except a list of items, which are defined as a specific type based on a "type" property on the model, and further distinguished by a "version" property. For example, we may receive a Ticket, or a User in this payload, and then if it is a Ticket, it may be a V1 or V2 ticket. This was working up until I attempted to label our top level interface as a sealed interface. Suddenly the type field was being filled with the version value on serialization. I attempted to come up with a minimal reproduction test, but I am not actually able to come up with a version that works (though without the sealed label it is now working in our code:
import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import org.junit.jupiter.api.Test
class TestStuff {
@Test
fun `test`() {
val writer = jacksonObjectMapper().writer().forType(jacksonTypeRef<List<AbstractData>>())
println(writer.writeValueAsString(listOf(StractV1("hello"), StractV2(15))))
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
JsonSubTypes.Type(value = Stract::class, name = "Stract")
)
interface AbstractData {
fun stuff(): String = "12"
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "version")
@JsonSubTypes(
JsonSubTypes.Type(value = StractV1::class, name = "1.0.0"),
JsonSubTypes.Type(value = StractV2::class, name = "1.1.0")
)
interface Stract : AbstractData
class StractV1(val id: String) : Stract
class StractV2(val number: Int) : Stract
The output of this code is: [{"type":"1.0.0","id":"hello"},{"type":"1.1.0","number":15}], but should be: [{"type":"Stract","version":"1.0.0","id":"hello"},{"type":"Stract", "version":"1.1.0","number":15}]. I was expecting that this would work with an interface, but would fail with a sealed interface, however, I have not been able to get it to work at all.
Versions Kotlin: 1.5.21 Jackson BOM: 2.12.1