FasterXML / jackson-modules-java8

Set of support modules for Java 8 datatypes (Optionals, date/time) and features (parameter names)
Apache License 2.0
399 stars 116 forks source link

If I have immutable object with only one field, I get exception during deserialization #231

Closed chudok closed 2 years ago

chudok commented 2 years ago

It is my POJO object for example

public final class Foo {
    private final Integer bar;

    public Foo(Integer bar) {
        this.bar = bar;
    }
}

It is ObjectMapper

       ObjectMapper mapper = new ObjectMapper()
                .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));

When I try read json,

Foo value = mapper.readValue("{\"bar\": 123}", Foo.class);

I get exception - com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of Foo (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

But! If I add second field in the Foo, all is working

public final class Foo {
    private final Integer bar;
    private final Integer bar2;

    public Foo(Integer bar, Integer bar2) {
        this.bar = bar;
        this.bar2 = bar2;
    }
...
}
...
Foo value = mapper.readValue("{\"bar\": 123}", Foo.class); // NO EXCEPTION

I assume that the problem is in this line.

https://github.com/FasterXML/jackson-databind/blob/c9e13bc4150e05566953ca570a67e8a84f652a0b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java#L499

Of course, everything will work if I put the annotation @JsonCreator on the constructor, but if I use lombok, I can't do this

@Value
public final class Foo {
    private final Integer bar;
}

jackson-databind 2.12.5 is used

cowtowncoder commented 2 years ago

Since it relies on parameter names module, need to move to different repo.

cowtowncoder commented 2 years ago

It looks like that should work with parameter names module, configuration setting.

The reason 2-parameter method works is due to 1-argument case being quite special (due to possible ambiguity), the issues between Properties and Delegating are exclusively for 1-argument case unfortunately.

rkeytacked commented 2 years ago

Of course, everything will work if I put the annotation @JsonCreator on the constructor, but if I use lombok, I can't do this

@chudok actually Lombok supports this since a long time. The older way:

@Value
@AllArgsConstructor(onConstructor_ = @JsonCreator)
public class Foo { // no "final" needed here, @Value adds it.
    Integer bar; // no "private final" needed here, @Value adds it.
}

The newer way:

@Value
@Jacksonized
public class Foo {
  Integer bar;
}

see https://projectlombok.org/features/experimental/Jacksonized, a feature since Lombok v1.18.14

chudok commented 2 years ago

Thank you for your answer! Yes, there are several options to do this, but I wanted to use as little code/annotations as possible.

rehevkor5 commented 2 years ago

The newer way:

@rkeytacked @Jacksonized doesn't do anything unless you also include a builder eg. via @Builder, which you might not want.

Also, I find that using @AllArgsConstructor(onConstructor_ = @JsonCreator) produces an exception like:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `com.example.MyClass`: Argument #0 has no property name, is not Injectable: can not use as Creator [constructor for com.example.MyClass, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}]