jackson-future-ideas icon indicating copy to clipboard operation
jackson-future-ideas copied to clipboard

Add JSON BodyHandlers/BodyPublishers support for the new Java 11 HttpClient

Open pragmasoft-ua opened this issue 5 years ago • 7 comments

Similar to the described in this SO thread

https://stackoverflow.com/questions/57629401/deserializing-json-using-java-11-httpclient-and-custom-bodyhandler-with-jackson

pragmasoft-ua avatar Jan 07 '20 21:01 pragmasoft-ua

I am not quite sure of the part that relates to Jackson here... could you elaborate?

cowtowncoder avatar Jan 08 '20 18:01 cowtowncoder

BodyHandler that can convert raw InputStream with JSON into a POJO. BodyPublisher that takes a POJO and converts it into a byte array with JSON.

In other words Java 11 HTTP client integration.

sergeykad avatar Jan 03 '21 13:01 sergeykad

This sounds like some kind of extension module (jackson-jr has couple, for example; and there are JAX-RS providers) or package, then, at least initially. Databind cannot have dependency to Java 11 quite yet.

cowtowncoder avatar Jan 03 '21 23:01 cowtowncoder

Small up because with all evolutions in Java 17 and 21 now, just using Jackson with the java client is something I do a lot, in addition with native compilation to get something simple & performant.

davinkevin avatar Nov 21 '23 22:11 davinkevin

As usual, PRs welcome! Could perhaps be added to jackson-modules-base or something. Does not belong to core (jackson-core, jackson-databind) components

cowtowncoder avatar Nov 22 '23 04:11 cowtowncoder

Would be great if someone can share solid usage example tho... like to what extent this "new module" should incorporate HttpClient ?

JooHyukKim avatar Nov 22 '23 15:11 JooHyukKim

I did that in Kotlin:

class JsonBody {
    companion object {
        val defaultObjectMapper = ObjectMapper().findAndRegisterModules()!!

        fun publisher(body: Any, objectMapper: ObjectMapper? = null): BodyPublisher {
            val mapper = objectMapper ?: this.defaultObjectMapper
            return BodyPublishers.ofString(mapper.writeValueAsString(body))
        }

        inline fun <reified T> handler(objectMapper: ObjectMapper? = null): BodyHandler<T> {
            val mapper = objectMapper ?: this.defaultObjectMapper
            val jsonNodeSubscriber = BodySubscribers.mapping(BodySubscribers.ofByteArray()) {
                mapper.readValue(it, T::class.java)
            }
            return BodyHandler { jsonNodeSubscriber }
        }
    }
}

And usage is very simple:

val request = HttpRequest.newBuilder()
            .uri(URI.create(instance.spec.url.toASCIIString() + "/api/v2/links"))
            .POST(JsonBody.publisher(linkBody))
            .header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
            .header("Authorization", token)
            .build()

return client.send(request, JsonBody.handler<LinkCreationResult>()).body()

It's part of a POC… I kept the opportunity to provide the ObjectMapper as parameter, but the companion has a default one too.

davinkevin avatar Nov 22 '23 15:11 davinkevin