Skip to content

Auto-detection of constructor-based creator method skipped if there is an annotated factory-based creator method (regression from 2.11)Β #2962

@hisener

Description

@hisener

Describe the bug
First, I am not sure whether it's a bug or expected behavior, but I just wanted to open an issue for my understanding. Prior to Jackson 2.12.0, it was possible to bind a Number (e.g., 42) to a single field DTO (see example code below). Now, it throws MismatchedInputException with this message:

Cannot construct instance of `jackson211$ExampleDto$Json` (although at least one Creator exists):
no int/Int-argument constructor/factory method to deserialize from Number value (42) at [Source: (String)"42"; line: 1, column: 1]

Version information
2.12.0

To Reproduce
The code below can be used.

Some notes:

  • I used jbang to switch between versions easily.
  • The example Dto is the minimal version of an Immutables generated code.
  • Jackson 2.11.3 uses the private constructor to deserialize Number to Dto object.
/// usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.fasterxml.jackson.core:jackson-annotations:2.12.0
//DEPS com.fasterxml.jackson.core:jackson-databind:2.12.0

// It prints `true` on 2.11.3.
////DEPS com.fasterxml.jackson.core:jackson-annotations:2.11.3
////DEPS com.fasterxml.jackson.core:jackson-databind:2.11.3

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.Objects;

public final class jackson211 {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    public static void main(String... args) throws JsonProcessingException {
        int version = 1;
        ExampleDto deserialize = OBJECT_MAPPER.readValue(String.valueOf(version), ExampleDto.class);
        System.out.println(new ExampleDto(version).equals(deserialize));
    }

    static final class ExampleDto {
        private final int version;

        private ExampleDto(int version) {
            this.version = version;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ExampleDto that = (ExampleDto) o;
            return version == that.version;
        }

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

        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
        static ExampleDto fromJson(Json json) {
            return new ExampleDto(json.version);
        }

        @JsonDeserialize
        @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
        static final class Json {
            int version;
            boolean versionIsSet;

            @JsonProperty
            public void setVersion(int version) {
                this.version = version;
                this.versionIsSet = true;
            }

            public int getVersion() {
                throw new UnsupportedOperationException();
            }
        }
    }
}

Expected behavior
As I wrote at the beginning, I am not sure whether it's a bug or expected behavior. If it's a bug then, the example code should print true instead of throwing an exception.

Additional context
N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions