-
-
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
@JsonCreator ignored when deserializing map keys #2158
Comments
I have the same issue. I would like an explicitly annotated @JsonCreator to have precedence over any String constructor. |
Handling of Map keys is a completely separate code path from handling of regular values, for better or worse (different kinds of serializer, deserializer are used), so that is probably the reason. |
Thanks for the reply. They are supported, the code to choose a string deserialiser - and I (at least) only care about deserialising from a simple string value - is in StdKeyDeserializers: public static KeyDeserializer findStringBasedKeyDeserializer(DeserializationContext ctxt,
JavaType type)
throws JsonMappingException
{
// We don't need full deserialization information, just need to know creators.
BeanDescription beanDesc = ctxt.introspect(type);
// Ok, so: can we find T(String) constructor?
Constructor<?> ctor = beanDesc.findSingleArgConstructor(String.class);
if (ctor != null) {
if (ctxt.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(ctor, ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
return new StdKeyDeserializer.StringCtorKeyDeserializer(ctor);
}
/* or if not, "static T valueOf(String)" (or equivalent marked
* with @JsonCreator annotation?)
*/
Method m = beanDesc.findFactoryMethod(String.class);
if (m != null){
if (ctxt.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(m, ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
return new StdKeyDeserializer.StringFactoryKeyDeserializer(m);
}
// nope, no such luck...
return null;
} so it first looks for a single arg constructor from String, then looks for a factory method. I'm trying a change where I just swap the order i.e. look for a factory method before the constructor against the current 3.0.0 snapshot but I'm blocked by an unrelated (at least, I can't imagine how it could be related) unit test failure atm so I've put my PR aside for a little while until I have time to debug that ( Would such an "swap the order" change be considered? Or is the risk of that breaking someone who's (unwittlingly) relying on a String constructor being preferred over a JsonCreator to high? Seems unlikely that would be the case as use of JsonCreator kind of implies coder wants that to be used for deserialisation. Cheers, Tim |
FYI, my use case is kind of like an extensible enum where I subclass CharSequence to make a kind of type safe String and only permit fixed set of values - same string value should resolve to same object instance so, while I have a constructor from String, it will fail when called a second time with the same value. So I have a valueOf creator that finds the existing instance if there is one and if not, only then constructs a new instance. |
Fixes FasterXML#2158 Simply swaps the order factory method and String constructors are searched for in the key type. Factory methods are now preferred.
Fixes FasterXML#2158 Simply swaps the order factory method and String constructors are searched for in the key type. Factory methods are now preferred.
I have same issues |
We hit this issue after enabling Immutables' interning functionality on some DTOs: lookups failed for deserialized maps with an interned key type. One workaround that appears to work is to declare a custom final class FactoryMethodKeyDeserializerModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
context.addKeyDeserializers(
(type, config, beanDesc) -> {
Method factoryMethod = beanDesc.findFactoryMethod(String.class);
if (factoryMethod == null) {
return null;
}
if (config.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(
factoryMethod,
config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
return new StdKeyDeserializer(-1, factoryMethod.getDeclaringClass()) {
@Override
public Object _parse(String key, DeserializationContext ctxt)
throws IllegalAccessException, InvocationTargetException {
// NB: `try/catch` can be dropped if FasterXML/jackson-databind#3109 is released.
try {
return factoryMethod.invoke(null, key);
} catch (InvocationTargetException e) {
if (e.getCause() != null) {
Throwables.throwIfUnchecked(e.getCause());
}
throw e;
}
}
};
});
}
} |
@Stephan202 thank you for sharing this! |
Back to the very beginning, I will note that as of Jackson 2.12, there is introspection for 2 String-based Creators, neither of which actually considers
However, javadocs also suggest that for (2), name must be I do think that |
Tnx! I happened to have sent you an email just over an hour ago with an attachment that contains some test cases that can be used for this purpose :) |
@Stephan202 that definitely would! We can add them under |
Ack! I can modify the code I sent by mail and open a PR. Won't have time for that right now; but can do tonight/tomorrow. 👍 |
@Stephan202 that'll be awesome, whenever -- I probably won't have time to look into this particular issue immediately anyway. But will be good to have reproduction. |
Alright, I filed #3111. |
I think this was solved along with #3143 -- test now passes. Fix will be included in 2.13.0 (since it required bigger refactoring of things unfortunately, cannot safely backport). |
I'm unsure whether this is a bug or expected behavior, but I found it a bit surprising. Given the following class:
Then, when a
Key
value is deserialized, the factory method annotated with@JsonCreator
is called as expected. IfKey
is used as a key in aMap
though, the factory method is not called and Key's private constructor is used directly instead.Key
is of course reduced to demonstrate the issue and hence lacking all properties that would make it actually useful as a key in a map.I tried the above with version 2.9.7. A repository demonstrating the issue can be found at https://github.com/Trundle/jackson-databind-oddity
The text was updated successfully, but these errors were encountered: