-
-
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
@JsonIdentityInfo
deserialization fails with combination of forward references, @JsonCreator
#1261
Comments
I've attached a new version of the test project that also includes forward references. You can see that problem is the same for the cyclical reference to the parent and for the forward reference. |
I think I'm experiencing a similar issue regarding deserialization and cyclic references - during deserialization, Jackson attempts to resolve some fields (like the |
What happens is that when an unresolved reference is encountered, an UnresolvedForwardReference is thrown. If you look at the code, this is only caught properly in CollectionDeserializer and MapDeserializer. It does not appear to be handled correctly in BeanDeserializer. So what happens is that when an UnresolvedReference is encountered as a field of a bean, an exception gets thrown that goes down the stack until a MapDeserializer or a CollectionDeserializer catch block is encountered. If there is no such deserializer on the stack, then the error message that eventually gets output is correct (a message about an unresolved forward reference). However if it gets caught in a MapDeserializer or CollectionDeserializer, then all hell breaks loose. Deserialization continues on the bean fields in JSON, but the deserializer thinks it's in a map or collection down the stack. So then nonsensical error messages get output about how the next field after the UnresolvedForwardReference is not compatible with the value type in the map or collection being deserialized. |
Lack of catching for beans is particularly puzzling since that is the main use case. |
Hmmh. So tests use Creators (constructors). That's typically something that does not mix very well with Object ids, so that's bit of a warning sign. But would explain why it could be an as-of-yet-not-working case. |
Yes. Unfortunately I cannot avoid using creators in my use case without very significant rewrites to my data model. So I am effectively blocked on this bug at work. Please let me know if there is anything else I can do to help. |
Oh and I should comment that the Creators don't seem to be a problem when the field is wrapped in a list (as in the test cases provided), so I'm hoping it won't be too hard to generalize what has been done for objects in maps and collections to beans. |
Looks like failure that I see is Fundamental theoretical problem with Creators is that not all cycles can be ever resolved: if a refers to b, and b refers to a (directly or indirectly), then only one of references can be passed via creator. Another thing to keep in mind is this: even when using I think I will try to see if locally modifying properties to use setters or fields would make specific test pass. That gives some information on where problems reside. |
But why does this case work properly for the parentAsList property, but not
|
@arifogel I agree. But I did want to mention eventual challenge in trying to use Creators for Object Id references, to make sure limitations are not a surprise. |
So what discipline do you recommend exactly?
|
@arifogel I would suggest using fields (or setters) for reference properties (except that Collection/Map values appear to be safe as per your comments), and creator for everything else. Nice thing about using fields is that it is possible to keep them private or protected. As to resolving issues with creator-passed references: looks like test case passes if only fields (or setters) are used, for what that is worth. I did not try anything fancier, just simple replacement. |
@arifogel I actually suspect the example case is indeed impossible (either in general, even with straight java; or with the Jackson deserialization works, more on that below): note that setup code itself does not pass So what object model needs to do is the same here; remove "favorite child" property from constructor, and leave setter. With that modification test passes locally for me. As to Jackson limitations: when dealing with Creators, all parameters must be resolvable when matching JSON Object is complete. Unlike with setter/field injection where deferral of Object Id resolution is possible with catching of exception, it can not be done with Creators because they can only be called once; and further Creator must be called to create the instance. For Collections this is different: they are not created using Creator, but simple no-arguments constructor; and elements may be added afterwards. It would be possible to force failure if custom Creator creator, taking all elements as array/Collection argument, was used; test does not do it and I don't think it is common usage pattern. I think this explains difference you saw wrt Collection/Map case compared to POJOs-with-creator. I'll try to think of better exception to throw, however; current message is not useful at all. |
Thanks! I don't think I need to set references in constructors in my main
|
@arifogel Right, I wanted to make sure there is a work-around. I believe there is still something off with handling, so I hope to play with the setup: I added a modified version as a failing test (one with creators used for everything). |
OK I implemented the workaround in my code, but I still ran into a similar problem as the one for which I produced the second patch in #1255. The problem is more or less the same: unresolved references for NON-CREATOR properties are properly lazily resolved when they are a value in a collection or map, but not when they are the NON-CREATOR property itself. In the latter case, the exception is caught too far below; the parser continues chomping away at the remaining properties in the bean with the unresolved reference, while the deserializer thinks it's at least one level up from said bean. See the new attached patch. I'll follow up later on with a modified small example. EDIT: |
On further reflection, I think it may make sense to combine this patch with the 2nd one from #1255, with one modification: the handleResolvedForwardReference function from the 2nd patch from #1255 (in the version of BeanReferring that deals with creator properties) should check to see if the value is null, and if so, throw an Exception stating that there is a cycle of final creator fields among objects. To be clear, the purpose of BOTH of these patches is to fix handling of unresolved forward references to objects that are direct bean properties so that they are [correctly] handled the same way as values in collections and maps.
applies to creator properties even when there are no cycles, then never mind about using the 2nd patch from #1255. |
Hmm.. I'm having trouble reproducing my problem with a small example. Better wait on this.. |
OK @cowtowncoder, I figured it out. My project was taking a different code path than the example you modified. I had JsonCreator functions that took a multitude of non-reference fields that were not being output initially because they were null-valued and I had @JsonInclude(Include.NON_NULL) set. Then when they were being deserialized, since not all creator properties were present, BeanDeserializer._deserializeUsingPropertyBased never thought that we were done with creator properties. As such, non-creator reference properties were deserialized using buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop)) in the same function. This call did not have proper handling for unresolved forward references, unlike the deserialize call after the comment "// or just clean?". My fix was to modify my code to not have creator properties that could be null-valued. So basically the patch I've provided in this issue added that handling to the later code path. I should note that if someone constructs JSON by hand with non-creator reference properties appearing before creator properties, this problem may still pop up. I don't think it's reasonable that field ordering in the JSON should impact execution. In fact, the current serialization code is ugly because it refuses to put fields strictly in alphabetical order, but rather puts creator properties first (I assume to prevent this problem). So I still think my patch (or something similar) should be applied, since it appears to enable arbitrary field ordering. |
Here is a small example demonstrating the error. Note that simply by adding '@JsonIgnore' to Child.getParent, you can avert the crash. |
Almost forgot to mention: when you apply my patch, then you get the output in with-json-ignore-getparent.txt even without adding the JsonIgnore annotation. This indicates that this is an inconsistency between handling of unresolved forward references in beans vs maps and collections. |
@arifogel Thanks. I agree in that ordering should not matter; serialization order is mostly optimization, not related to correctness of deserialization (but helps in common case as deserializer can avoid possibly costly buffering; just does not count on it). My main concern with original patch was just that adding second place for handling should not be done to cover other problems, so it'd be good to know why initial handling for bean properties was not working. I guess I still don't fully understand that. But I hope looking through examples helps. I think @pgelinas implemented original handling so I'll see if he might have suggestions as well. |
In updated tests there seems to be some problem with type resolution, so that array of |
@JsonIdentityInfo
deserialization fails with combination of forward references, @JsonCreator
@arifogel After reading through the latest patch it is nice and small, and does fix the issue! Thank you very much for going through the code and figuring out the solution to this problem. It goes in 2.8.0 (.rc2); I am bit hesitant to try to backport it in 2.7. |
As a follow-up to bug #1255, the patch I provided exposes related deserialization problems.
I have attached a small project ('jackson-test.zip') to demonstrate these issues. When run with both patches from #1255, the output is provided in the attached 'both.txt'. When run with just the first patch from #1255, the output is provided in the attached 'first.txt'.
Important points:
jackson-test.zip
both.txt
first.txt
The text was updated successfully, but these errors were encountered: