Skip to content

Instantly share code, notes, and snippets.

@jcraane
Last active June 28, 2022 14:05
Show Gist options
  • Save jcraane/20ec33de7f27000c073255c542530af7 to your computer and use it in GitHub Desktop.
Save jcraane/20ec33de7f27000c073255c542530af7 to your computer and use it in GitHub Desktop.
Jackson serialization issue with sub/super types
package nl.anwb.geocontext
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.databind.DatabindContext
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase
import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
fun main() {
val mapper = XmlMapper(JacksonXmlModule().apply {
setDefaultUseWrapper(false)
}).registerKotlinModule()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val docs = Documents().apply {
document = DocumentSet().apply {
this.documents.add(NormalDocument("name"))
this.documents.add(SpecialDocument("author"))
}
}
val xml = mapper.writeValueAsString(docs)
println(xml)
val document = mapper.readValue(xml, Documents::class.java)
println(document)
}
@JacksonXmlRootElement(localName = "Documents")
class Documents {
@JacksonXmlProperty
var document: Document? = null
override fun toString(): String {
return "Documents(document=$document)"
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type")
@JsonTypeIdResolver(value = MyTypeResolver2::class)
open class Document {
@field:JacksonXmlProperty(isAttribute = true, localName = "type")
var type: String? = null
}
class DocumentSet : Document() {
@JacksonXmlProperty(localName = "Document")
var documents: ArrayList<Document> = ArrayList()
}
data class NormalDocument(
@JacksonXmlProperty
val name: String? = null,
) : Document()
data class SpecialDocument(
@JacksonXmlProperty
val author: String? = null,
) : Document()
class MyTypeResolver2 : TypeIdResolverBase() {
private var superType: JavaType? = null
override fun init(baseType: JavaType?) {
superType = baseType
}
override fun idFromValue(value: Any?): String {
return when (value) {
is DocumentSet -> "set"
is SpecialDocument -> "special"
is NormalDocument -> "normal"
else -> ""
}
}
override fun idFromValueAndType(value: Any?, suggestedType: Class<*>?): String {
// Not used
return ""
}
override fun getMechanism(): JsonTypeInfo.Id {
return JsonTypeInfo.Id.CUSTOM
}
override fun typeFromId(context: DatabindContext, id: String): JavaType {
println("ID = $id")
return when (id) {
"normal" -> context.constructSpecializedType(superType, NormalDocument::class.java)
"special" -> context.constructSpecializedType(superType, SpecialDocument::class.java)
"set" -> context.constructSpecializedType(superType, DocumentSet::class.java)
else -> context.constructSpecializedType(superType, Document::class.java)
}
}
}
@jcraane
Copy link
Author

jcraane commented Jun 28, 2022

Fails with:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class nl.anwb.geocontext.Document]: missing type id property 'type' (for POJO property 'Document')
at [Source: (StringReader); line: 4, column: 23] (through reference chain: nl.anwb.geocontext.Documents["Document"]->nl.anwb.geocontext.DocumentSet["Document"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.InvalidTypeIdException.from(InvalidTypeIdException.java:43)
at com.fasterxml.jackson.databind.DeserializationContext.missingTypeIdException(DeserializationContext.java:2083)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingTypeId(DeserializationContext.java:1596)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._handleMissingTypeId(TypeDeserializerBase.java:307)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedUsingDefaultImpl(AsPropertyTypeDeserializer.java:185)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:96)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1292)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:357)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:214)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:186)
at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:122)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:144)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:110)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1292)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
at com.fasterxml.jackson.dataformat.xml.deser.XmlDeserializationContext.readRootValue(XmlDeserializationContext.java:91)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3598)
at nl.anwb.geocontext.XmlListTestKt.main(XmlListTest.kt:24)

@jcraane
Copy link
Author

jcraane commented Jun 28, 2022

Does work with the newest 2.13.3 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment