-
-
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
Fix #4938 Allow @JsonCreator
to return null
#4948
base: 2.19
Are you sure you want to change the base?
Conversation
return ctxt.handleInstantiationProblem(handledType(), null, | ||
_creatorReturnedNullException()); | ||
// [databind#4938] 2025-Feb-02, Since 2.19, Allow returning `null` from creator | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure this is sufficient -- couple of issues not covered by tests:
- Unknown properties collected during (see line 469/472 below) need to be processed
- This may be called in the middle of Object value and following properties need to be handled or skipped (otherwise parent deserializer gets control back in the middle of child Object value
I suspect that for (2) we need to either skip all content, or call handleUnknownProperty()
all that may remain.
But the first thing is to write tests I think. Note, too, that it is possible to have properties that are not Creator properties. Those cannot be assigned when null
is returned (but skipping probably makes sense).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, if bean is null
, I am not sure if there is anything else we can do than return, unless there is some sort of post-processing that can be done to a null
value that I forgot to consider?
Regardless, added test case via 332ac77.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't do anything to null
, but we must consume any other properties that may have been buffered (or just drop), but more importantly, ones that may come after Creator properties are collected: collection stops when value for everything needed is gathered which may be before JSON Object is fully read.
So, f.ex: @JsonCreator
-annotated constructor (for Pojo
that matches "pojo1") takes "a" and "b", and we have:
{
"pojo1" : {
"a": 2,
"z": 4, // unknown, will be buffered
"b": 3, // Creator called here when "a" and "b" gotten
"c": 4 // but we MUST consume "c" and value, and END_OBJECT!
} ,
"pojo2": {
"a":3,
// and so on
}
}
then Creator is called half-way through reading Object value of pojo1
. But it cannot just leave without making sure full Object value is processed. All it can do is skip (cannot set), but without doing that, following deserializers will be called at weird/wrong state. Every deserializer must consume one full JSON value (either by itself, or "child" deserializers it calls).
I hope this makes more sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Added a test case with InputStream
- And also modified the
BeanDeserializer
to not return null right away
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to address case of properties other than those matched as Creator properties: ones collected before Creator called and ones that may come after it's called.
return ctxt.handleInstantiationProblem(handledType(), null, | ||
_creatorReturnedNullException()); | ||
// [databind#4938] Since 2.19, Allow returning `null` from creator, but | ||
// only after doing the regular handling of buffered properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No,not quite -- cannot do regular handling since we have no bean
. Instead, call a new method which will handle skipping, passing till the end.
.without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) | ||
.readValue(in); | ||
// Check if read all the bytes | ||
assertEquals(-1, in.read()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach does not work: Jackson parsers buffer content so all content being read from InputStream
does not actually necessarily mean all has been consumed.
There is a method in JsonParser
for pushing back "unread" content but...
Instead of this, you could just enable DeserializationFeature.FAIL_ON_TRAILING_TOKENS
to verify there is no more content to be read (tokens). That should catch the issue.
@cowtowncoder If you see current build failure there are two tests failing trying to handle unknown properties in following ways...
Both reaches BeanDeserializer.handleUnknownVanilla(), could it be what we already want? Or are you saying we need the execution to continue to go on further down, to like ... ![]() ... here? |
I think that if But that handling should be extracted to a helper method just to keep logic cleaner and possibly change. And pass already collected information. |
@cowtowncoder Just some updates,
|
Alright, I think I'm stuck at trying to find the proper place/timing for method extraction. No rush for this work to be done, but any opinion much appreciated! 😆 |
I'm happy to have a look -- might take a while as I'm going for 1 week vacation tomorrow. But will help when I get back (unless I have extra time today). |
resolves #4938