question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

JsonCreator on static method in Enum and Enum used as key in map fails randomly

See original GitHub issue

given 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:closed
  • Created 3 years ago
  • Reactions:7
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
cowtowncodercommented, May 19, 2020

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_NAMEs, not VALUE_STRINGs).

1reaction
BigMichi1commented, May 19, 2020

I upgraded to 2.11.0 but the behaviour is still the same, sometimes it works, sometimes it fails

Read more comments on GitHub >

github_iconTop Results From Across the Web

Serializing enums with Jackson - java - Stack Overflow
Finally I found solution myself. I had to annotate enum with @JsonSerialize(using = OrderTypeSerializer.class) and implement custom serializer:
Read more >
Security update for jackson-databind, jackson-dataformats ...
... with JsonCreator Value vs Array + JsonCreator on static method in Enum and Enum used as key in map fails randomly +...
Read more >
Writing REST Services with RESTEasy Reactive - Quarkus
RESTEasy Reactive is a new JAX-RS implementation written from the ground up to work on our common Vert.x layer and is thus fully...
Read more >
Index (Apache Avro Java 1.7.7 API)
Constructs an iterator over key-value map entries out of a generic iterator. ... ENUM - Static variable in class org.apache.avro.io.parsing.
Read more >
Management & Monitoring - Micronaut Documentation
A low-level HTTP client is provided which you can use to test the HelloController created in the previous section. Testing Hello World. Java...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found