-
-
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
ObjectMapper.registerSubtypes(NamedType...)
doesn't allow to register the same POJO for two different type ids
#2515
Comments
Missing Other than that that sounds reasonable as I thought behavior of multiple names mapping to same class was already allowed and working. Also tagging as "good first issue" as fix seems simple enough assuming no complications arise. |
ObjectMapper.registerSubtypes(NamedType...)
doesn't allow to register the same POJO for two different type ids
If @mbaechler isn't working on this, then I'd like to give it a shot if that's alright. |
@jkosh44 sounds good to me. |
I've just been thinking about this issue and have a question. Lets say we have the following two classes (borrowed from some of the tests)
Then from the Deserialization side of things I understand that we should expect the following to create two similar objects of type
However from the Serialization side of things what would be the expected behavior of the following:
What should the value of s be? |
Hi @jkosh44 ,
That means the type is an actual field of the POJO, so there's no ambiguity. |
I think that in case of one class with two ids, on serialization side, one obvious outcome would be Exception: this is unsupported case. It would be asymmetric and as you point out would only work for deserialization. This with existing system as-is. If system was to be improved, I suppose one approach could be to allow "aliases" as secondary accetable ids on deserialization. This would specify primary id explicitly. However, this would require much more significant changes. |
So the current behavior for my example above is that
I'm not sure how common of a use case this is but there are tests for it, and all the suggestions above do break this use case. One possible solution is that instead of assuming that the first id defined is the primary/principal id, we assume that the most recent id defined is the primary/principal id. The fact that there is caching and you need a new ObjectMapper for this use case leads me to think it's not valid and is just erroneously being tested for. I thought it was worth bringing up though. |
In terms of the use case @mbaechler brought up for this issue, hashing on the class and name seems to take care of deserialization case. However when we serialize a field we rely on |
@jkosh44 To claierfy: registering of subtypes absolutely must be done before any use, and it is not something that can be done after use -- this is explicitly declared as "does not work" wrt "config-then-use" pattern. Behavior will not be changed; in 3.0, however, it will not be possible to even try to change things when mappers are built using builder pattern. This hopefully removes confusion as I can see how current behavior can be unexpected. So question goes back to what to do with duplicates for specific use case: I think it should be one of:
|
Gotcha, I think I misunderstood the test I was referencing. It wasn't testing overriding a previous call to In terms of what to do about duplicates, I think option 2 feels more intuitive, however I think right now option 1 is the better option. I think this because that's the current behavior today and because of how caching works. When |
Before this commit NamedType hashes only on it's class and not on the name. This only allowed you to register a class once using ObjectMapper.registerSubtypes(NamedType... types). This commit now uses name field to hash NamedTypes. This successfully allows you to deserialize objects of the same type but different name. Serializing objects of the same type but different name (for example fields of a POJO) still has some issues. 1. SerializerProvider caches Serializers based on class and doesn't take name into account. 2. TypeIdResolver has no method to resolve an id that takes name into account. Therefore when resolving an id we only have the value and type. 3. TypeNameIdResolver stores type and id information as a Map<String, String> that maps type to id, ignoring name Fixes FasterXML#2515
Before this commit NamedType hashes only on it's class and not on the name. This only allowed you to register a class once using ObjectMapper.registerSubtypes(NamedType... types). This commit now uses name field to hash NamedTypes. This successfully allows you to deserialize objects of the same type but different name. Serializing objects of the same type but different name (for example fields of a POJO) still has some issues. 1. SerializerProvider caches Serializers based on class and doesn't take name into account. 2. TypeIdResolver has no method to resolve an id that takes name into account. Therefore when resolving an id we only have the value and type. 3. TypeNameIdResolver stores type and id information as a Map<String, String> that maps type to id, ignoring name Fixes FasterXML#2515
Before this commit NamedType hashes only on it's class and not on the name. This only allowed you to register a class once using ObjectMapper.registerSubtypes(NamedType... types). This commit now uses name field to hash NamedTypes. This successfully allows you to deserialize objects of the same type but different name. Serializing objects of the same type but different name (for example fields of a POJO) still has some issues. 1. SerializerProvider caches Serializers based on class and doesn't take name into account. 2. TypeIdResolver has no method to resolve an id that takes name into account. Therefore when resolving an id we only have the value and type. 3. TypeNameIdResolver stores type and id information as a Map<String, String> that maps type to id, ignoring name Fixes FasterXML#2515
I may not have explained (2) properly: I do not mean that you would be able to change definitions, but rather that in case of multiple calls during initialization phase, this would be checked. At any rate, both options are possible and about as easy to implement. |
Before this commit NamedType hashes only on it's class and not on the name. This only allowed you to register a class once using ObjectMapper.registerSubtypes(NamedType... types). This commit now uses name field to hash NamedTypes. This successfully allows you to deserialize objects of the same type but different name. Serializing objects of the same type but different name (for example fields of a POJO) still has some issues. 1. SerializerProvider caches Serializers based on class and doesn't take name into account. 2. TypeIdResolver has no method to resolve an id that takes name into account. Therefore when resolving an id we only have the value and type. 3. TypeNameIdResolver stores type and id information as a Map<String, String> that maps type to id, ignoring name Fixes FasterXML#2515
Before this commit NamedType hashes only on it's class and not on the name. This only allowed you to register a class once using ObjectMapper.registerSubtypes(NamedType... types). This commit now uses name field to hash NamedTypes. This successfully allows you to deserialize objects of the same type but different name. Serializing objects of the same type but different name (for example fields of a POJO) still has some issues. 1. SerializerProvider caches Serializers based on class and doesn't take name into account. 2. TypeIdResolver has no method to resolve an id that takes name into account. Therefore when resolving an id we only have the value and type. 3. TypeNameIdResolver stores type and id information as a Map<String, String> that maps type to id, ignoring name Fixes FasterXML#2515
Thank you very much for this fix |
registerSubtypes
takes an array ofNamedType
s:But unfortunately, if I want to register the same POJO for several types (let's say the JSONs only differ on their type field), it will only register the first one because they are added to a
LinkedHashSet
and NamedType doesn't includename
field inequals
/hashcode
.I don't even create a subtype of NamedType to override
equals
/hashcode
because the class it final.Would it make sense to care about
NamedType.name
field ?The text was updated successfully, but these errors were encountered: