org.http4k.format.jacksonExtensions.kt Maven / Gradle / Ivy
package org.http4k.format
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.KeyDeserializer
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.module.kotlin.KotlinModule
import dev.forkhandles.data.JsonNodeDataContainer
import dev.forkhandles.values.Value
import org.http4k.core.Body
import org.http4k.lens.BiDiBodyLensSpec
import org.http4k.lens.BiDiMapping
import org.http4k.lens.ContentNegotiation
import java.math.BigDecimal
import java.math.BigInteger
fun KotlinModule.asConfigurable(mapper: T): AutoMappingConfiguration =
object : AutoMappingConfiguration {
override fun int(mapping: BiDiMapping) = adapter(mapping, JsonGenerator::writeNumber, JsonParser::getIntValue)
override fun long(mapping: BiDiMapping) = adapter(mapping, JsonGenerator::writeNumber, JsonParser::getLongValue)
override fun double(mapping: BiDiMapping) = adapter(mapping, JsonGenerator::writeNumber, JsonParser::getDoubleValue)
override fun bigInteger(mapping: BiDiMapping) = adapter(mapping, JsonGenerator::writeNumber, JsonParser::getBigIntegerValue)
override fun bigDecimal(mapping: BiDiMapping) =
adapter(mapping, JsonGenerator::writeNumber, JsonParser::getDecimalValue)
override fun boolean(mapping: BiDiMapping) = adapter(mapping, JsonGenerator::writeBoolean, JsonParser::getBooleanValue)
override fun text(mapping: BiDiMapping) =
adapter(mapping, JsonGenerator::writeString, JsonParser::getText)
.apply {
if (mapping.clazz.isEnum) {
addKeySerializer(mapping.clazz, object : JsonSerializer() {
override fun serialize(value: OUT, gen: JsonGenerator, serializers: SerializerProvider) =
gen.writeFieldName(mapping(value))
})
addKeyDeserializer(mapping.clazz, object : KeyDeserializer() {
override fun deserializeKey(key: String, ctxt: DeserializationContext) = mapping(key)
})
}
}
private fun adapter(
mapping: BiDiMapping,
write: JsonGenerator.(IN) -> Unit,
read: JsonParser.() -> IN
) =
apply {
addDeserializer(mapping.clazz, object : JsonDeserializer() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): OUT =
mapping.invoke(p.read())
})
addSerializer(mapping.clazz, object : JsonSerializer() {
override fun serialize(value: OUT, gen: JsonGenerator, serializers: SerializerProvider) =
gen.write(mapping(value))
})
}
override fun done(): T = mapper.apply { registerModule(this@asConfigurable) }
}
/**
* Prevent the unmarshalling of unknown values4k types.
*/
fun AutoMappingConfiguration.prohibitUnknownValues(): AutoMappingConfiguration =
text(BiDiMapping(
Value::class.java,
{ error("no mapping for ${it.javaClass}") }, { error("no mapping for ${it.javaClass}") })
)
/**
* Custom lens to extract and inject Data4k DataContainer types from JSON bodies
*/
fun Body.Companion.json(
fn: (JsonNode) -> T,
description: String? = null,
contentNegotiation: ContentNegotiation = ContentNegotiation.None
): BiDiBodyLensSpec = Jackson.body(description, contentNegotiation).map(fn, JsonNodeDataContainer::unwrap)