Is there any clever way to handle null in a JSON b...
# http4k
m
Is there any clever way to handle null in a JSON body lens? I want a Kotlin
null
value to be mapped to/from a JSON
null
value.
d
Assuming you mean Jackson, then this is controlled by the underlying configuration of the mapper
m
I am actually using Gson.
d
Of all of the libraries, GSON plays the worst with Kotin. I'd recommend Moshi if you have a choice 🙃
m
I am using Gson since it is already used in the project (Moshi is not), and I want to avoid bring in more dependencies than necessary. But if Moshi can solve this problem better than Gson, then I can consider it. Do you have an example on how to do it with Moshi (I have not used Moshi before)?
d
+1 on the dependencies 🙂 . Are we talking about null fields or an entirely null body?
m
entirely null body.
d
and we're talking about an empty string when we say "null"? that's quite a curious response instead of a 404
m
No, a JSON null value (encoded as string "null"). Otherwise an JSON object ("{...}").
d
it's a tricky one for sure - I take it you're not in charge of that API (it seems pretty broken to me!). The problem with the auto marshalling is that they are bound to
Any
and not
Any?
.
You could write a filter to detect the content length and turn it into a 404
m
Yes, I am converting an existing API (with existing clients) from another framework (SparkJava) to http4k. The API is (manually) documented here: https://gitlab.com/chromaway/postchain/-/blob/dev/doc/restapi/postchain-restapi.yaml
d
Copy code
val client = Filter { next ->
        {
            next(it).let { if (it.header("Content-Length") == "6") Response(NOT_FOUND) else it }
        }
    }.then(JavaHttpClient())
then you can pretend that their API isn't utterly broken! 😂
m
Well, I can't change the existing clients that easily 🤷 . We have clients in JavaScript and other languages as well.
d
sorry - are you trying to recreate this "null" or consume it?
m
Recreate, I am working on the server side.
d
ok - you definitely can't do it nicely with the standard lens system because the lens is designed to do nothing if it encounters a null value. You'll have to do something manual I'm afraid (although you can obviously wrap the behaviour up in an reusable extension function somewhere and use that in those endpoints.
m
Ah, OK.
I did it like this (with GSON):
Copy code
val fooBody = Body.auto<Foo>().toLens()
val nullBody = Body.json("null JSON").map(
            {
                require(it.isJsonNull) { "null JSON expected" }
            },
            {
                com.google.gson.JsonNull.INSTANCE
            }
    ).toLens()
Copy code
"/foo/{fooId}" bind GET to { request ->
                val fooId = fooIdPath(request)
                val foo: Foo? = fooDB.getFoo(fooId)
                if (foo != null) {
                    Response(OK).with(fooBody of foo)
                } else {
                    Response(OK).with(nullBody of Unit)
                }
            },
And it works, I even managed to wrap it in a
ContentNegotiation
since I have another format than JSON to serve as well (with similar
null
semantics).
378 Views