-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
For an absent property Jackson injects NullNode
instead of null
to a JsonNode-typed constructor argument of a @ConstructorProperties
-annotated constructor
#3214
Comments
I cannot think of a way to change this behavior, except for custom But I hope to have a look at this before 2.13.0 is released to double-check my understanding. Null replacement itself is needed mostly for primitive values; I think I agree that it should not be used for |
Ok, the following
Thanks for the clue that it should be possible with Tested with Jackson 2.7.5 and 2.12.4 |
A unit test to cover the main possible cases:
|
I will have to think about this a bit more -- may consider change in 2.14, but too late for anything in 2.13. |
Just don't break the workaround, please :-) |
@robvarga :) Usage as shown should still work. Another work-around, if you don't mind using |
We have the same issue starting from 2.11. Koltin class containing nullable property is desirialized as NullNode instead of null. For example: |
One problem here wrt Kotlin is just that Kotlin has additional nullability metadata over Java. So the question of |
sure, @cowtowncoder, finally I open a PR in jackson-databind-kotlin: FasterXML/jackson-module-kotlin#491. |
I'll let Kotlin module maintainers to decide that: I think it is something worth documenting. The original use case for |
Agree, so let us debate 😄 |
It is worse. Even in Java, it is only deserialized as null (not set) if the property was not part of those injected via the chosen constructor/creator. If the property is among those passed to the chosen constructor, it is deserialized as NullNode, that is what my bug complained about. I agree, that it should be null, as that represents absent property better when explicit null is represented by NullNode. An alternative which could be considered to avoid NPE's is representing absent property as MissingNode. |
I don't disagree with above; and yes, it only affects cases where property value is piped through Creator method. My only concerns are that I want to
Right now the practical problem is that I have very little time to work on OSS (this is my hobby, not daytime job) which slows down progress. But I hope to get this resolved before 2.13.0 final. I appreciate all the help so far, let's keep discussion going. One possibility I am thinking of is to add |
Hmmmh. There is actually one immediate failure (if changing If worse comes to worst it might even be necessary to add a hack into handling of Creator arguments to avoid "un-nulling" absent value for specific case of @novtor As to round-trippability of |
Which would be the correct thing to do. Since the constructor invocation do not have the ability to not take absent properties therefore you have to differentiate between absent and explicit null properties with different values passed to that parameter. And since if you declared the attribute ObjectNode or ArrayNode instead of JsonNode then you cannot receive NullNode in the first place anyway, so passing null instead of NullNode to properties declared as JsonNode actually makes behaviour of JsonNode identical to behaviour of ObjectNode/ArrayNode.
I am not sure exactly what you mean with this, but IMHO, if the question of how to interpret a serialized null comes up at all in a round-trip scenario, that means that your serialization is the cause of the problem as that loses the information in the first place whether the object which was serialized had a null or a NullNode in the property by serializing both possibilities the same way. |
I am now thinking of adding |
My concern is that JsonNode and 'JsonNode?' are not the same thing. Personnaly, I would even say that Optional and 'Optional?' should behave differently even if it is a nonsense to declare a field as 'Optional?' Let us imagine the kotlin cases. I am omitting the fields with default value as it should be strightforward, if a field is missing, it is given the default value, otherwise it should behave as for special or regular values:
For Java cases, as there is no non-nullable types, everything should behave as nullables in kotlin. And finally, @robvarga 's point about NullNode serialization, I join her/him. Probably the issue is there: should it be serialized as an empty string or {} but not as null ? |
I think we can join here this one also #2705 |
On |
I guess this is one way of handling the backward compatibility aspect, but ideally it should be supported by the corresponding out-of-the-box enums used in annotations to declaratively indicate the possible behaviours without having to write our own JsonDeserializer. |
Agreed that eventually it'd be nice to have more granular mechanisms, and that requiring to provide one's own custom deserializers is not much of an extension point. So the focus here is solely to solve the immediate problem. Later on if there is need for further customizations it'd be possible to discuss value and effort needed for other extension points. |
I agree that a present NullNode property value should be serialized to an included null in the JSON. Let's look at it from the other end, the Java class, too. This may tie in somewhat to #2430 which I noticed when browsing around, which I believe was the cause of absent properties being converted to But in my opinion, it is wrong in default behaviour to coerce absent properties to some other values (but #2458 does just that) since they are not null values to start with. Particularly, since they are not coerced when they are populated via setters. It is particularly wrong to coerce them, if serialization inclusion for the created type is set Include.NON_NULL only since it implies that whatever is absent was null originally. |
@robvarga I think you are arguing with something else than what I am saying. :) I agree that handling of absent values is distinct and separate from handling of incoming I hope to have enough time to work on this now and possibly solve the specific case of |
NullNode
instead of null
to a JsonNode-typed constructor argument of a @ConstructorProperties
-annotated constructor
Ok, proposed fix checked in 2.13 branch -- if anyone has time to check, that would much appreciated. One tricky part, which seems to work but is... bit fragile... is that of Null replacement (Nulls.FAIL, Nulls.AS_EMPTY) and so on -- that will still treat absent values as equivalent to nulls. This because users rely on that behavior for null avoidance. My main concern there is just the precedence of handling; all tests pass but I suspect there may be usage cases where combinations might not work as expected. Still, I think this is solid improvement for now. |
Oh, also: it may well make sense to change behavior for |
Describe the bug
When using @ConstructorProperties-annotated constructor to deserialize type, for an attribute of type JsonNode which is absent from the deserialized JSON, a NullJsonNode is passed to the constructor instead of null reference.
I would like to know if there is a workaround (e.g. some annotation indicating what to do for absent properties, or configuring some alternative deserializer on a per-property basis which is able to deal with this) for this issue which works with a constructor and final attributes instead of having to resort to non-final attributes and setters.
Since our project is on 2.7.5 at the moment, I would particularly be intereted if there is a workaround for 2.7.x which allows me to inject null instead of this NullNode at least on a per object basis if not on a per-property basis.
If there is no workaround, then this should probably be fixed or a workaround should be provided which works with final attributes and @ConstructorProperties-annotated constructor.
Version information
Which Jackson version(s) was this for?
Reproduced with 2.12.4, 2.9.8 and 2.7.5
To Reproduce
If you have a way to reproduce this with:
Expected behavior
The unit test fails as res.getNodeFromConstructor() returns a NullNode instead of null.
It should return null identically to the property which is setter-populated. (The setter does not get called).
Additional context
The text was updated successfully, but these errors were encountered: