jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

Cannot use both JsonCreator.Mode.DELEGATING and JsonCreator.Mode.PROPERTIES static creator factory methods in Enum, but can for Class

Open andrewbents opened this issue 1 year ago • 0 comments

Describe the bug When Enum has two factory methods, one with JsonCreator.Mode.DELEGATING and the other with JsonCreator.Mode.PROPERTIES, only the latter works. Deserialization that is supposed to target the DELEGATING one fails with com.fasterxml.jackson.databind.exc.MismatchedInputException. Note that the same setup for a POJO works just fine.

Version information 2.13.3

To Reproduce

class TestCases {
    @Test
    void testClass() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Assertions.assertEquals(new AClass("someName"), objectMapper.readValue("{ \"name\": \"someName\" }", AClass.class));
        Assertions.assertEquals(new AClass("someName"), objectMapper.readValue("\"someName\"", AClass.class));
    }

    @Test
    void testEnum() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Assertions.assertEquals(AEnum.A, objectMapper.readValue("{ \"type\": \"AType\" }", AEnum.class));
        Assertions.assertEquals(AEnum.A, objectMapper.readValue("\"AType\"", AEnum.class)); // this line fails
    }
}

class AClass {
    private final String name;

    AClass(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static AClass fromString(String name) {
        return new AClass(name);
    }

    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public static AClass create(@JsonProperty("name") String name) {
        return new AClass(name);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AClass aClass = (AClass) o;
        return Objects.equals(name, aClass.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
enum AEnum {
    A("AType"),
    B("BType");

    private final String type;

    AEnum(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static AEnum fromString(String type) {
        return Arrays.stream(values())
                .filter(aEnum -> aEnum.type.equals(type))
                .findFirst()
                .orElseThrow();
    }

    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public static AEnum create(@JsonProperty("type") String type) {
        return fromString(type);
    }
}

The testClass passes, but testEnum fails with

com.fasterxml.jackson.databind.exc.MismatchedInputException: Input mismatch reading Enum `AEnum`: properties-based `@JsonCreator` ([method AEnum#fromString(java.lang.String)]) expects JSON Object (JsonToken.START_OBJECT), got JsonToken.VALUE_STRING

Also, you can remove the PROPERTIES factory method, and the DELEGATING method would work.

andrewbents avatar Aug 05 '22 07:08 andrewbents