projectlombok / lombok

Very spicy additions to the Java programming language.
https://projectlombok.org/
Other
12.82k stars 2.38k forks source link

Lombok 1.16.20 and Jackson 2.9.4 produces Jackson MismatchedInputException #1612

Open johnnorton opened 6 years ago

johnnorton commented 6 years ago

Given this class

@RequiredArgsConstructor()
public class ResponseObject
{
  public final String id;
}

And this simple code:

 final String json = "{\"id\":\"123\"}";
 final ObjectMapper objectMapper = new ObjectMapper();
 final ResponseObject responseObject1 = objectMapper.readValue(json, ResponseObject.class);

Produces the following exception:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `dropbear.ResponseObject` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"id":"123"}"; line: 1, column: 2]

Switching back to 1.16.18 resolves the error. Any Suggestions?

randakar commented 6 years ago

This is highly likely to be caused by the fact that later versions of lombok do not put a @Generated annotation on the generated constructor. Mostly because java 9 puts that annotation semi-out of reach. There is a lombok property to put that back, or you can put the @JsonCreator annotation on the constructor by hand.

Disclaimer: I am not a member of the lombok team, I just lurk here ;-)

On Thu, Mar 15, 2018 at 5:45 PM, John Norton notifications@github.com wrote:

Given this class

@RequiredArgsConstructor() public class ResponseObject { public final String id; }

And this simple code:

final String json = "{\"id\":\"123\"}"; final ObjectMapper objectMapper = new ObjectMapper(); final ResponseObject responseObject1 = objectMapper.readValue(json, ResponseObject.class);

Produces the following exception:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of dropbear.ResponseObject (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{"id":"123"}"; line: 1, column: 2]

Switching back to 1.16.18 resolves the error. Any Suggestions?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rzwitserloot/lombok/issues/1612, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKCRdIgC0WHmZwnu8EPR_9PJSHB1oHuks5tepq5gaJpZM4SsgNP .

-- "Don't only practice your art, but force your way into it's secrets, for it and knowledge can raise men to the divine." -- Ludwig von Beethoven

johnnorton commented 6 years ago

Tried but no dice. Thanks for the suggestion.

mszabo-wikia commented 6 years ago

Have you tried out the @ConstructorProperties annotation? Jackson 2.9.4 should be able to make use of that:

@RequiredArgsConstructor(onConstructor = @__({@ConstructorProperties({"id"})}))
randakar commented 6 years ago

@JsonCreator should work too, I think.

Op 16 mrt. 2018 23:28 schreef "Máté Szabó" notifications@github.com:

Have you tried out the @ConstructorProperties annotation? Jackson 2.9.4 should be able to make use of that:

@RequiredArgsConstructor(onConstructor = @__({@ConstructorProperties({"id"})}))

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/rzwitserloot/lombok/issues/1612#issuecomment-373861993, or mute the thread https://github.com/notifications/unsubscribe-auth/AAKCRRrkgYXu7YaITEFY5tz6-iwWF3gPks5tfDykgaJpZM4SsgNP .

xMandrake commented 6 years ago

Similar problem with spring boot upgrade 1.5.8 -> 2.0.0 The behavior of creating classes has changed

Class example:

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class A {
    @JsonProperty("id")
    private final long id;
}

Parsing json string:

A item = new ObjectMapper().readValue("{'id' : 10}", A.class);

Lombok 1.16.18 + Jackson 2.8.10 - OK Lombok 1.16.20 + Jackson 2.9.4 - Cannot construct instance of A (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

mcherb commented 6 years ago

i have the same problem when i migrated to v = 1.16.20.

java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of xx.xxx.xxx.Actions: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

but it works if i add the followings to lombok.config file:

lombok.anyConstructor.addConstructorProperties=true config.stopBubbling = true

rspilker commented 6 years ago

As @mcherb menioned, in lombok.config you can add:

lombok.anyConstructor.addConstructorProperties=true

This is also mentioned in the changelog.

rspilker commented 5 years ago

Is this still an issue?

chandresh-pancholi commented 5 years ago

Yes

rspilker commented 5 years ago

@chandresh-pancholi Can you explain how adding lombok.anyConstructor.addConstructorProperties=true to your lombok.config does not solve your problem?

rspilker commented 5 years ago

Since v1.18.8 lombok added better understanding of @JsonProperty and @JsonSetter

bric3 commented 4 years ago

Using @Value does not copy the @JsonProperty annotation on the constructor fields, however destructuring @Value has the intended behavior i.e. to copy the @JsonProperty on the constructor.

Here's the setup

config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

# https://github.com/rzwitserloot/lombok/issues/1563
# due to JDK9 compatibility lombok introduced a breaking change by removing the
# java.beans.ConstrustorProperties (because it belongs to java.desktop) from the
# generated constructors, the issue indicate one shoud add the following property
#
# lombok.anyConstructor.addConstructorProperties = true
#
# however it will still require anyone to import java.desktop, which is not
# what we want for a server. The issue can be avoided if the code uses a
# constructor compiled with the -parameters option which is the case here.
# So it is possible to keep this option to false.
#
# However jackson had special understanding of the ConstructorProperties for his
# dynamic way to guess how to instanciate some classes. Without this jackson failed
# to find the constructor, so some classes needed a small help by adding the @JsonCreator
# or add @JsonProperty on the constructor parameter (this issue helped me in the matter
# https://github.com/FasterXML/jackson-databind/issues/1318)

lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonFormat

Adding lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty has no effect.

Using `@Value` only ❌ ```java @Value public class AppleToken { @JsonProperty("apple_token") String appleToken; } ``` ```java public final class AppleToken { @JsonProperty("apple_token") private final String appleToken; @Generated public AppleToken(final String appleToken) { this.appleToken = appleToken; } @Generated public String getAppleToken() { return this.appleToken; } @Generated public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof AppleToken)) { return false; } else { AppleToken other = (AppleToken)o; Object this$appleToken = this.getAppleToken(); Object other$appleToken = other.getAppleToken(); if (this$appleToken == null) { if (other$appleToken != null) { return false; } } else if (!this$appleToken.equals(other$appleToken)) { return false; } return true; } } @Generated public int hashCode() { int PRIME = true; int result = 1; Object $appleToken = this.getAppleToken(); int result = result * 59 + ($appleToken == null ? 43 : $appleToken.hashCode()); return result; } @Generated public String toString() { return "AppleToken(appleToken=" + this.getAppleToken() + ")"; } } ```
adding `@JSonCreator(mode = Mode.PROPERTIES)` on the constructor ❌ ```java @Value @AllArgsConstructor(onConstructor = @__(@JsonCreator(mode = Mode.PROPERTIES))) public class AppleToken { @JsonProperty("apple_token") public final String appleToken; } ``` ```java public final class AppleToken { @JsonProperty("apple_token") private final String appleToken; @Generated public String getAppleToken() { return this.appleToken; } @Generated public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof AppleToken)) { return false; } else { AppleToken other = (AppleToken)o; Object this$appleToken = this.getAppleToken(); Object other$appleToken = other.getAppleToken(); if (this$appleToken == null) { if (other$appleToken != null) { return false; } } else if (!this$appleToken.equals(other$appleToken)) { return false; } return true; } } @Generated public int hashCode() { int PRIME = true; int result = 1; Object $appleToken = this.getAppleToken(); int result = result * 59 + ($appleToken == null ? 43 : $appleToken.hashCode()); return result; } @Generated public String toString() { return "AppleToken(appleToken=" + this.getAppleToken() + ")"; } @JsonCreator( mode = Mode.PROPERTIES ) @Generated public AppleToken(final String appleToken) { this.appleToken = appleToken; } } ```
Destructuring `@Value` ✅ ```java @Getter @FieldDefaults(makeFinal=true, level= AccessLevel.PRIVATE) @EqualsAndHashCode @ToString @AllArgsConstructor public class AppleToken { @JsonProperty("apple_token") String appleToken; } ``` ```java public class AppleToken { @JsonProperty("apple_token") String appleToken; @JsonProperty("apple_token") @Generated public String getAppleToken() { return this.appleToken; } @Generated public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof AppleToken)) { return false; } else { AppleToken other = (AppleToken)o; if (!other.canEqual(this)) { return false; } else { Object this$appleToken = this.getAppleToken(); Object other$appleToken = other.getAppleToken(); if (this$appleToken == null) { if (other$appleToken != null) { return false; } } else if (!this$appleToken.equals(other$appleToken)) { return false; } return true; } } } @Generated protected boolean canEqual(final Object other) { return other instanceof AppleToken; } @Generated public int hashCode() { int PRIME = true; int result = 1; Object $appleToken = this.getAppleToken(); int result = result * 59 + ($appleToken == null ? 43 : $appleToken.hashCode()); return result; } @Generated public String toString() { return "AppleToken(appleToken=" + this.getAppleToken() + ")"; } @Generated public AppleToken(@JsonProperty("apple_token") final String appleToken) { this.appleToken = appleToken; } } ```
Destructuring `@Value`, with public final field and without getter ✅ ```java //@Value @FieldDefaults(makeFinal=true, level= AccessLevel.PUBLIC) @EqualsAndHashCode @ToString @AllArgsConstructor public class AppleToken { @JsonProperty("apple_token") String appleToken; } ``` ```java public class AppleToken { @JsonProperty("apple_token") public final String appleToken; @Generated public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof AppleToken)) { return false; } else { AppleToken other = (AppleToken)o; if (!other.canEqual(this)) { return false; } else { Object this$appleToken = this.appleToken; Object other$appleToken = other.appleToken; if (this$appleToken == null) { if (other$appleToken != null) { return false; } } else if (!this$appleToken.equals(other$appleToken)) { return false; } return true; } } } @Generated protected boolean canEqual(final Object other) { return other instanceof AppleToken; } @Generated public int hashCode() { int PRIME = true; int result = 1; Object $appleToken = this.appleToken; int result = result * 59 + ($appleToken == null ? 43 : $appleToken.hashCode()); return result; } @Generated public String toString() { return "AppleToken(appleToken=" + this.appleToken + ")"; } @Generated public AppleToken(@JsonProperty("apple_token") final String appleToken) { this.appleToken = appleToken; } } ```

edit: I'm not sure but I may have been played by the gradle cache when toggling the lombok.config (adding lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty)

rajmondx commented 2 years ago

This is an extremely annoying issue.

I tried all provided solutions/hacks here (except using @JsonProperty because it would just take too much effort) and also tried to use the prev. generated class files as well but failed so far.

rzwitserloot commented 2 years ago

@rajmondx We fixed it - see @rspilker's comment about adding addConstructorProperties to the config. If this fix doesn't work we need detailed information on what doesn't work. Alternatively, you'll have to wait for us to learn jackson, get familiar with it, attempt to figure out what the bug is that you're running into, and then fix it. We're kinda swamped, so I would expect that to take at least 4 years.

In other words, somebody familiar with JsonProperty needs to do the legwork. Make a project or jar or zip that can be used to readily reproduce the problem, which either stands on its own or includes a readme that spells out, starting with a vanilla java install and no knowledge of jackson, how to run it and what the desired result is. Explain why adding lombok.anyConstructor.addConstructorProperties=true doesn't work, or at least investigate and give us something on that topic.

For example, @bric3 talks about 'destructuring' but I have no idea what that means.

We have a few contributors that do appear to use jackson on a daily basis and they are not reporting any issues right now.

janrieke commented 2 years ago

There's also a StackOverflow topic that covers this issue.