How do I configure jackson object mapper?
Using Jackson and Spring to configure and Object mapper with Kotlin.
When there is a problem trying to deserialize or serialize an object from Kotlin into json, you may need to create your own way to customize the json serialization process. In order to do this in Spring and Kotlin, you’ll need to create a configuration for Jackson that will override the ObjectMapper
builder.
Configuration class to customize Jackson:
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer
@Configuration
class JacksonConfiguration {
@Bean
fun customizer(): Jackson2ObjectMapperBuilderCustomizer {
return Jackson2ObjectMapperBuilderCustomizer { builder -> builder.modules(myModule()) }
}
@Bean
fun myModule() : Module {
return MyModule()
}
}
In the above class, we specify a MyModule
that will override the specifics by using the built in Jackson Module approach. This will add to the behavior of Jackson without completely overriding all the defaults. The MyModule
will add a custom deserializer that will help with the problem areas of your deserialization issues.
My Module example class:
import com.fasterxml.jackson.databind.Module
import com.fasterxml.jackson.databind.module.SimpleModule
class MyModule : SimpleModule() {
override fun setupModule(context: Module.SetupContext) {
super.setupModule(context)
context.addDeserializationProblemHandler(JsonProblemHandler())
}
}
The handler is the key part that is used to address the changes needed for the json deserializing. It extends the DeserializationProblemHandler
to handle the issues that aren’t handled by the default deserialization in Jackson. In this case, we look for a specific object that is part of an object chain. The object chain would look something like this:
class MyAbstractEntity {
}
class MyEntity : MyAbstractEntity {
}
When we see the concrete class we make sure the base type is returned instead of following the default path. This type of approach can also be used to override property deserialization, etc… with different handlers and listeners added to the module context.
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver
class JsonProblemHandler : DeserializationProblemHandler() {
override fun handleMissingTypeId(
ctxt: DeserializationContext?,
baseType: JavaType?,
idResolver: TypeIdResolver?,
failureMsg: String?
): JavaType {
// return base class if specific type is found
if(baseType != null && baseType.isTypeOrSuperTypeOf(MyEntity::class.java)) {
return baseType
}
return super.handleMissingTypeId(ctxt, baseType, idResolver, failureMsg)
}
}
Some more ideas can be seen over on Stack Overflow’s q & a related to the above.
Another option for the above problem could be Overriding the TypeIdResolver.