Skip to content

Custom JSON marshallers

Gradle setup

compile group: "org.http4k", name: "http4k-core", version: "3.255.0"
compile group: "org.http4k", name: "http4k-format-jackson", version: "3.255.0"

Custom auto-mapping JSON configurations

http4k declares an extended set of "primitive" types which it can marshall out of the box - this includes the various http4k primitives (Uri, Status), as well as a bunch of common types from the JDK such as the DateTime classes and Exceptions. These primitives types cannot be marshalled as top-level JSON structures on their own so should be contained in a custom wrapper class before transmission.

You can declare your own custom marshaller by reimplementing the Json instance and adding mappings for your own types - either uni or bi-directional.

This ability to render custom types through different JSON marshallers allows API users to provide different "views" for different purposes - for example we may wish to hide the values of some fields in the output, as below:

Code

package cookbook.custom_json_marshallers

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.KotlinModule
// this import is important so you don't pick up the standard auto method!
import cookbook.custom_json_marshallers.MyJackson.auto
import org.http4k.core.Body
import org.http4k.core.Response
import org.http4k.core.Status.Companion.OK
import org.http4k.core.with
import org.http4k.format.ConfigurableJackson
import org.http4k.format.asConfigurable
import org.http4k.format.text
import org.http4k.format.withStandardMappings

object MyJackson : ConfigurableJackson(KotlinModule()
    .asConfigurable()
    .withStandardMappings()
    // declare custom mapping for our own types - this one represents our type as a simple String
    .text(::PublicType, PublicType::value)
    // ... and this one shows a masked value and cannot be deserialised (as the mapping is only one way)
    .text(SecretType::toString)
    .done()
    .deactivateDefaultTyping()
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
)

data class PublicType(val value: String)
data class SecretType(val value: String) {
    override fun toString(): String {
        return "****"
    }
}

data class MyType(val public: PublicType, val hidden: SecretType)

fun main() {
    println(
        Response(OK).with(Body.auto<MyType>().toLens() of MyType(PublicType("hello"), SecretType("secret")))
    )

    /** Prints:

    HTTP/1.1 200 OK
    content-type: application/json; charset=utf-8

    {"public":"hello","hidden":"****"}

     */
}