jsonb-api
jsonb-api copied to clipboard
Handling of JSON input `null` is unspecified
Behavior of passing input "null" in Jsonb.fromJson("null", T) is unspecified.
Section 3.6 defines to return (type) null for unspecified output type, but this is only very limited case.
I propose following behavior:
Add section 3.14.3 "Null input", which reapplies the requirement for java fields to top level deserialization:
"The deserialization operation of JSON document consisting solely of null value results in null Java value. The exception are target types java.util.Optional, OptionalInt, OptionalLong, OptionalDouble. In this case the result is empty optional value. "
To make serialization and deserialization symmetrical, the change should affect serialization of top level empty optional as well. In section 3.4.3, "Empty optional instances are serialized as null. ". This is extension of current wording that only covers array items.
One more step to symmetry would be to allow Jsonb.toJson accept null input.
The new section 3.14.3 can also specify:
"Serialization of Java null value results in JSON Document null".
Hi
I agree there is a lack and we should enhance it. But please take care null can be desired to be skipped which means that at root level we would have an inconsistency forcing its materialization.
Some impl treat it as a null object - or a container with no attribute - and represent it as {} to avoid to break consumers.
It probably needs some refinements and an overall definition of how null is handled depending where it is and jsonbconfig?
Wdyt?
But please take care null can be desired to be skipped which means that at root level we would have an inconsistency forcing its materialization.
The focus of this issue is deserializing, when null is already present as sole input, so the choice have been made not to skip it or represent it as {} on the wire. It also doesn't change any current field or property serialization behavior.
I agree that the other options are valid serialization decisions, but they bring their own issues:
If null serializes into "", it produces unparseable input and on the wire makes sense only if you're using jsonb outside its default usecase, for example by making stream of JSON documents. I believe the in such case application is capable handling null values before invoking JSON-B. But could be an option if also fromJSON can handle empty input.
If input is {} then under current rules it will not deserialize into empty Optional, rather into Optional.of(new TargetType()). Treating {} as null is quite inconsistent with behavior for fields and properties and I believe is best solved with custom deserializer and/or application level logic.
Probably in order to support all of these strategies we would need:
- default behavior (and I humbly offer mine as default ;))
- option for skipping top level null/empty (and deserializing empty document into null / empty optional). And to define what happens if JSONB receives input
nullwith this option enabled.
Such behavior is also consistent with Javascript:
JSON.stringify(null)
> "null"
JSON.parse("null")
> null
JSON.parse("")
> SyntaxError: JSON.parse: unexpected end of data
I dont have a computer handy but is it consistent for undefined too? If so sounds ok as default while configurable somehow.
With undefined we get into one of the strong areas of Javascript -- consistency:
JSON.parse("undefined")
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
JSON.stringify(undefined)
undefined
But json.org / ECMA-404 doesn't mention any handling of value undefined, so JSON-B should not worry about that.
For configurability I can imagine
JsonConfig.withNullSerializer(JsonbSerializer<Object> nullSerializer)
It would be called whenever null or empty optional is passed to toJson(T). This enables it to serialize as any of the options you listed. Default implementation would emit null.
It's hard to think about straightforward API for deserialization as the three options presented are too different from parsing perspective, especially already parsed empty object is not fit to call to deserializer. Probably defining suppliers would work for these three cases: withNullDeserializer(Supplier<Object> nullSupplier), withEmptyInputDeserializer(Supplier<Object> emptySupplier), withEmptyObjectDeserializer(Supplier<Object> emptySupplier). Default implementations would return null, throw an exception and be not defined, meaning, that standard JSON-B process of creating default instance should be used.