-
-
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
Deserialize Json Array that contin 2 objects with the same ID - JsonMappingException: Already had POJO for id #266
Comments
You can send your QUESTIONS to mailings lists. Issue trackers are for reporting problems and requesting new features. |
User mailing list found at: |
Guys this is a bug. It is not a question! I can confirm that this is broken from Jackson 2.4 that added ObjectIdResolver. These is a bug and should be fixed! |
Add this test to TestXmlID2 tests. It will fail
|
You are trying to use two objects with same object id -- that would seem to be your bug right there. Test for XmlID shows the same problem, duplicate id (test I assume is one from JAXB annotations module). |
This is not a bug. You have two users that are in the same department. When Jackson is trying to deserialize the data you get this error because it finds the same department id in two users. The serialization works fine. |
Input JSON has two full department definitions, both with id 9. Use of JSON Object is taken to mean POJO, and references MUST use simple scalar values. So from Jackson perspective you do have conflicting definitions, two departments that claim to have same id. I understand that you might want behavior to be such that full objects could be passed, and somehow Jackson figured out which one to use. But this is not how Object Id works, which is why I do not think this is a bug as is. |
Why you don't think this is a bug. If I run this test putting only the id of department in the second instance it will not fail. But as you understand we want to pass the object with all the values or only with the id from the client side to the JSON provider. If you cannot resolve it, can you suggest an alternative or a way to disable this feature? @test |
I explained it already. Logic for choosing whether a given value is a full object, or object reference, is simple:
What you can not do is expect some kind of magic to figure out that JSON Objects sometimes are full values, and sometimes references. This is not supported by object id handling. If you do not want to use Object Ids, then remove |
My problem is that in you validation for duplicate ids you have one scope for all objects inside the root object you receive. It's object inside the root object and each object in a collection item of root object should have his own scope. You throw this validation because in SimpleObjectIdResolver you keep all the inner objects of root object which is totally wrong. |
Jackson allows you to specify different scopes with public class Container {
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="@id",
scope=OwnerScope.class)
public User owner;
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="@id",
scope=UserScope.class)
public User user; Class used for scope has no semantic meaning, it's simply just some value -- and by default, it defaults to type of thing being referenced. The challenge with XML is whether semantics of While I understand that things may not work for your use case, or the way you want, it is good to keep in mind that there are many ways scoping could or should work for different users. As to JPA, other tools: they have their semantics, definitions; and likewise Bean Validation uses whatever rules you give. What they do has very little relevance for this case. |
I also ran into this issue and one work-around I found is to supply a custom DefaultDeserializationContext implementation to the object mapper. Specifically I implemented the "findObjectId" method to not bother caching the objects it finds but instead to just simply return a new "ReadableObjectId" instance each time. btw, The main motivation I see for such a use case is where a application is sending back denormalized json documents, which is in fact my use case. It would be nice if a "DeserializationFeature" was added to prevent the validation check from causing the deserializer to blow-up. |
@jfitzpatrick99 if you do not want Jackson to manage Object Ids, you probably should not indicate those fields are ids -- the only reason for annotations is to automatically connect object instances via ids; and for that reason ids absolutely must be retained and resolved; and duplicate ids are errors unless used in different scopes. The original request of somehow automatically de-duping objects is something Jackson does not do, and is unlikely to support. |
In my case, I am not interested in automatically de-duping objects, I just don't want Jackson to blow-up during deserialization if it encounters an object with a duplicate ID. My use case is that I want to be able to deserialize data that may either contain references to objects only, e.g. references that are always serialized by ID only, or references that are expanded in-line in the json document. To accomplish this, I wrote a custom Jackson serializer that supports in-line expansion of selected referenced entities which is enabled or disabled based on the value of a query parameter. By default, I have Jackson configured to always serialize references by ID only. It would be nice if Jackson supported the ability to ignore duplicates to help with this use case. I suppose I could do what you suggest and remove the @JsonIdentityInfo annotation but then I have to implement more custom logic to serialize the ID field as a reference myself. If I understand how Jackson does scoping correctly, it does not seem possible to use scoping to make deserialization work for the case where a list of objects contains references to nested objects where two or more nested objects share the same ID. |
Correct. Jackson Object Id handling is design explicitly to support Object Identity. It will not currently bend to cases of "maybe unique, maybe not" hybrid schemes. Assuming this was to be improved to allow other schemes: how should it work? Without retaining ids at all, it seems of little use -- what would ids be resolved to? With multiple choices, however, which one should it link to? First one in document order? I will reopen this, since maybe there is something to improve here, if this really is a commonly used pattern. I did not think it was, but since this keeps on coming up... |
For my particular use case, the resulting deserialized object graph is denormalized so that I literally end up with multiple instances of the same object. This works well for me and might work well for others. If this approach were to be incorporated into Jackson, I would suggest adding a "DeserializationFeature" such as "DENORMALIZE_DUPLICATE_OBJECT_IDS". Obviously the default would be to have the feature disabled but if enabled, Jackson would simply ignore objects with same ID during deserialization and create multiple object instances representing the same object. For implementation, it would just be necessary to change the implementation of the DefaultDeserializationContext.findObjectId according to whether the feature is enabled. Although I am not super familiar with the Jackson code base so there may be a better place to make this change. |
Ok let me try to rephrase this: are you NEVER getting id back? And the only reason to enable Object Id handling is for serialization, where you do want to remove duplicate; but some other systems produce different kind of output? But if you want the first instance to be serialized as full object, secondaries as ids, then As to ignored |
For our application, we either send back the id of the referenced object only, or if requested by the client, we "expand" the referenced object so that the entire object is returned. For example, in our application users own an object called stream. Streams can be made public or remain private. If a stream is made public then other users may access the stream. By default, when a list of streams is serialized, I want that only the id of the owning user is returned. However, if a stream has been made public, it is required for us to also include (limited) information about the user who owns the stream. This means that when deserializing this data, we will likely end up with duplicate user objects in the resulting json document. In our case, the user objects will actually be expanded partial representations of the user object for security purposes. To meet these requirements, it is convenient to simply add @JsonIdentityReference(alwaysAsId = true) to the user reference and then override this behaviour in a custom serializer according to the value of a query parameter. Maybe there is a better way of achieving what I want to do? Incidentally I don't like the idea of only serializing the full object for the first user object having a given ID because that makes it more difficult for the client to process the data, i.e. client now has to keep track of which objects having a given ID have been received and reestablish the links. I am ok with the memory/increased data size trade-off for the reduction in complexity.
Supporting both duplicates and resolution is not what I need but I agree that someone may want this. I think that for now, simply supporting duplicates would be sufficient.
Exactly, except I think it would probably be best to add a deserialization feature mutually exclusive to "DENORMALIZE_DUPLICATE_OBJECT_IDS" that enables this behavior. |
@jfitzpatrick99 |
@aokegbile, The issue has not been addressed in Jackson but there is a work-around. What you can do is extend the com.fasterxml.jackson.databind.deser.DefaultDeserializationContext class and override the findObjectId method to always return a new ReadableObjectId instance.
Then you just need to configure the object mapper to use your custom DefaultDeserializationContext implementation. This can be done by supplying your custom context implementation to the object mapper constructor. Hope that helps. |
@aokegbile Since this is not a bug but a feature, I closed this particular issue. But there is (or should be?) a separate issue for adding a feature to allow easily altering the behavior, for cases where automated strict matching is not desired. |
Thank you both... I'm new and will attempt the fix Jfitz suggested. |
Hey guys. Was something ever introduced into the library to accommodate this use case or is the fix from Jfitz still the solution people are using? |
I don't think anything was yet added. |
@jfitzpatrick99 'findObjectId' method is not getting called. Is there something else that I have to do? |
@nareshr8, Before I can help you, you need to supply more information about what you have tried. The check list is:
Note that there are other methods in the ObjectMapper class that can change the "DefaultDeserializationContext" instance in use. You should also make that sure than one of these methods is not causing the context to be swapped out underneath you. To mitigate this you should also override the fluent "DefaultDeserializationContext" factory methods. If none of that helps, I suggesting using the debugger to confirm that your object mapper is using the instance you expect and go from there. |
Ran into this not so long ago. |
@Adonai quick question here: in your case, what is the reason for using I am still trying to fully understand actual use case. It is possible there are multiple ones, but at least one of them. I do understand the case of id-strings not being unique (but not necessarily ever resolved); I can understand different scopes (which can handled via |
@cowtowncoder just common sense. I have a framework that sends JSONs like this
The id numbers on |
@Adonai Thank you for clarification. Ok so the problem is not duplicates per se, but the representation of ids. Thing is, Jackson does not thing you are passing an ID -- it is an Object, and Jackson does not support composite Object keys, mostly because there is no simple and reliable way to detect difference between Objects and references to Objects. This problem will not go away simply by allowing duplicate ids. Rather it will create "empty" Object values, which is unlikely to be what users want. Now, for JSOG, there is an actual extension that can handle specific kind of "mini-object", where there is exactly one key/value pair, and key is fixed to specific value. This mechanism could be used to handle usage described above. If this is what is wanted, a new issue should be filed, and we could figure out how to support that via annotations and/or API. |
@cowtowncoder you got me absolutely right. I'll file a different issue tomorrow. |
Link to Jackson JSOG module: |
I'm not sure if this remains an open issue or not, but I figured it might help to have a minimal project with an obvious use-case. So I've cut down a project I'm working on here to be as small as I can make it. I want to make a library API. Where users can add Books, which have a ManyToMany relationship with Authors. A book may have multiple Authors, and an Author may write multiple books, so if we serialize a book to JSON and include all of the information about all of its Authors, and then we serialize all of those, and then they all have information about their Books, so then we serialize all those... An Author is very simple, the name of the author is the ID. That's it. So we could POST a book like this, which would make a book, and map it to an author, which we may already have in the database, or which we might be making from her scratch:
Works great! But then we would need to make one HTTP request per book, which could incur a performance constraint. So we want to have another endpoint for adding lots of them:
Unfortunately this will be a problem, because Jackson will deserialize the two "JK Rowling" separately, and bomb out, and even though they are the exact same object, Jackson doesn't understand how to map them together. My solution was to have the two objects simply resolve to be the same thing. The exact same object reference. In the Entity:
The ObjectIdResolver class:
I'll upload the final project code in a sec here. |
I've made a Gist of the solution I made: |
Thanks, its work for me |
How can I deserialize an Array with two objects that contains the same SubObject with the same Id?
For example this is the JSON of the array
As you can see both objects has the same related package. I want that the deserializer only parse the first relatedPackage and use it for the secon relatedPackage.
My Pojos are the next:
User Class:
RelatedPackage Class:
And finaly the main Object wich realize the deserializtion:
When both relatedPackage has the same Id it sends me the next error message
I understand that it is trying to create a second object with the same objectId.
How can I solve that problem?
The text was updated successfully, but these errors were encountered: