Last active
June 28, 2022 14:05
-
-
Save jcraane/20ec33de7f27000c073255c542530af7 to your computer and use it in GitHub Desktop.
Jackson serialization issue with sub/super types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} |
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
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)