Emil Orvik Kollstrøm
10/16/2019, 8:26 PMalex
10/16/2019, 9:13 PMStephan Schroeder
10/17/2019, 9:50 AM@Configuration
class ImporterConfig {
@Bean
fun getXmlMapper(): XmlMapper = XmlMapper(JacksonXmlModule().apply {
setDefaultUseWrapper(false)
setXMLTextElementName(XMLTextName)
// blank string to null
addDeserializerForType<String> { parsed: String? ->
if (parsed != null && parsed.isNotBlank()) {
parsed
} else {
null
}
}
// Boolean should also be converted from values "0"(false) and "1"(true)
addDeserializerForType<Boolean> { parsed: String? ->
parsed?.let{
when(it.toLowerCase()) {
"0", "false" -> false
"1", "true" -> true
else -> null
}
}
}
}).apply {
registerModule(KotlinModule())
configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
companion object {
const val XMLTextName = "xmlTextName"
}
}
private inline fun <reified T : Any> JacksonXmlModule.addDeserializerForType(crossinline convert: (String?) -> T?) {
this.addDeserializer(T::class.java, object : StdDeserializer<T>(T::class.java) {
override fun deserialize(parser: JsonParser, context: DeserializationContext): T? {
val result: String? = StringDeserializer.instance.deserialize(parser, context)
return convert(result)
}
})
}
I’m pretty proud of my inline extension function with reified type parameter addDeserializerForType
🤓Stephan Schroeder
10/17/2019, 9:55 AMXMLTextName
is for, it’s for when you need to deserialise a tag that has a body, but also attributes. Assume you have a tag <Price currency="EUR" >23.99</Price>
than the data class for that would look like this:
data class Price(
val currency: String,
@JacksonXmlText @JsonProperty(XMLTextName) val amount: Double
)
Emil Orvik Kollstrøm
10/17/2019, 6:59 PM