arrow-integrations
arrow-integrations copied to clipboard
Add support for generic tristate (undefined | null | defined) codec
Feature Request
- add support for serialization / deseiralization of
TriState<A>
which modelsAbsent | Null | Defined<A>
Context
There are times where users need to model an API that need differentiate between:
- absence of a field
- presence of a field in json, but being set to null
- presence of a field in json, with a value set
An example is a PATCH endpoint that may have the following contract:
// modify nickname
PATCH /api/user/{id} { "nickname" : "john doe" }
// deletes nickname
PATCH /api/user/{id} { "nickname" : null }
// does nothing
PATCH /api/user/{id} { }
Currently nullables T?
is not a viable alternative as they are missing a degree of freedom (the absence) and hence isn't usable. As well, Option<T>
is generally used to treat missing and absence as None by convention. While Option<T?>
is likely to be a possible container type for this, it is somewhat not feasible to be used due to null being a synthetic type. I.e. somehow Jackson could not differentiate whether the type signature requested is nullable or not at runtime, or at least I could not find a way to do so with my limited knowledge.
A possible alternative would be to define a TriState<A>
as follows:
sealed class TriState<out A> {
companion object
object Absent : TriState<Nothing>()
object Null : TriState<Nothing>()
data class Defined<T>(val value: T) : TriState<T>()
}
Given the code above we'd have a concrete distinction between Absent
or Null
that Jackson can use. This makes modelling the above behaviour possible. Additionally, this model can be bi-directionally converted to Arrow's Option<T?>
or Option<Option<T>>
without loss of precision which hopefully should support the ergonomic and integration with the rest of the arrow ecosystem.
Another alternative would be to expose a module that users can use to implement such sealed class within their own codebase e.g. GenericTriStateModule<T>
.
Hello @myuwono, I just quickly read this issue and that make me think of jackson-databind-nullable. I don’t know if you already check this module but perhaps it can help.
Thank you @christophejan I wasn't aware of that project. That one is interesting. JsonNullable<T>
does have the similar objective, approached using using the Java Optional-ish approach instead of Kotlin sealed class. I was initially on the fence whether or not a new type would be necessary. After looking into that library, it appears that a new dedicated type would indeed be a good addition to arrow-integrations-jackson. We will need a good name for it.