JsonCreator on static method in Enum and Enum used as key in map fails randomly
See original GitHub issuegiven this piece of code and jackson-databind 2.10.3
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
public static void main(String[] args) throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
final Map<TestEnum, String> input = new HashMap<>();
input.put(TestEnum.FOO, "Hello");
final String inputJson = mapper.writeValueAsString(input);
System.out.println("inputJson = " + inputJson);
final Map<TestEnum, String> output = mapper.readValue(inputJson, new TypeReference<Map<TestEnum, String>>() {
});
System.out.println("output = " + output);
}
private enum TestEnum {
FOO(1);
private final int i;
TestEnum(final int i) {
this.i = i;
}
@JsonValue
public int getI() {
return i;
}
@JsonCreator
public static TestEnum getByIntegerId(final Integer id) {
return id == FOO.i ? FOO : null;
}
@JsonCreator
public static TestEnum getByStringId(final String id) {
return Integer.parseInt(id) == FOO.i ? FOO : null;
}
}
}
and running it on multiple computers the output is different.
sometimes it works and the map is deserialized correctly and sometimes it does not work and fails with Exception in thread "main" java.lang.IllegalArgumentException: Parameter #0 type for factory method ([method foo.Test$TestEnum#getByIntegerId(1 params)]) not suitable, must be java.lang.String
now when I change the order of the methods annotated with JsonCreator it works, but then it started to fail on other computers with the same exception as above.
I tried to debug this and found out that sometimes the order of the annotated methods is different that is iterated over in BasicDeserializerFactory#_createEnumKeyDeserializer
when taking a look at the beanDesc.getFactoryMethods()
methods. These list of methods as far as I could see is created in AnnotatedCreatorCollector#_findPotentialFactories
via ClassUtil.getClassMethods
which then calls ClassUtil.getDeclaredMethods(cls)
and this is using Class#getDeclaredMethods
which as written in the JavaDoc that the methods elements in the returned array are not sorted and are not in any particular order.
So this is now probably the root of the issues which I can see. And as the loop stops with the exception after the first candidate has been checked in BasicDeserializerFactory
the remaining methods are not taken into consideration.
From my perspective, all annotated methods need to be checked before the exception can be thrown or at least the annotation parser should fail when multiple creators are detected.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:7
- Comments:6 (4 by maintainers)
Top GitHub Comments
I can reproduce this locally, added a failing test. Seems easy enough to change not to fail on first ineligible
@JsonCreator
in this particular case – and reason is specifically that the other creator is valid when deserializing Enum values, just not as Map key.This is bit messy area, as division between “values” and “Map keys” is not well modeled by Jackson; but it stems from JSON only having Strings as key values, and corresponding need to limit value set, as well as requirements for actual (de)serializers (as Map keys are exposed as
FIELD_NAME
s, notVALUE_STRING
s).I upgraded to 2.11.0 but the behaviour is still the same, sometimes it works, sometimes it fails