简体   繁体   中英

Deserialising problem with lombok builder and jackson, cannot construct instance of [...] no String-argument constructor/factory method to deserialize

This is a simplified version of a problem I've been having. Given these classes:

@Value
@Jacksonized
@Builder(builderClassName = "Builder", setterPrefix = "with")
public class Limits {
  Limit minimum;
  Limit maximum;
}
@Value
@Jacksonized
@Builder(builderClassName = "Builder", setterPrefix = "with")
public class Limit {
  @JsonValue
  String value;
}

and this code:

Limits limits = Limits.builder()
  .withMinimum(Limit.builder().withValue("One-Day").build())
  .withMaximum(Limit.builder().withValue("One-Year").build())
  .build();

System.out.println(objectMapper.writeValueAsString(limits));

it works as expected and gives me the following output:

{
  "minimum": "One-Day",
  "maximum": "One-Year"
}

However, when I try to deserialise the same JSON string, as follows:

String json = """
  {"minimum":"One-Day","maximum":"One-Year"}
  """;

objectMapper.readValue(json, Limits.class);

I get the following error:

Cannot construct instance of `Limit$Builder` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('One-Day')
at [Source: (String)"{"minimum":"One-Day","maximum":"One-Year"}"; line: 1, column: 12] 

Is there a way to make it work without changing the data model or the JSON?

I tried adding @JsonCreator to the Builder of Limit as follows, but gives the same error

@Value
@Jacksonized
@Builder(builderClassName = "Builder", setterPrefix = "with")
public class Limit {
  @JsonValue
  String value;

  public static final class Builder {
    @JsonCreator
    public Builder withValue(String value) {
      this.value = value;
      return this;
    }
  }
}

Appreciate any input on what I might be missing here.

Jackson does not support @JsonCreator in combination with builders. @JsonCreator basically says: The annotated method constructs a new instance, passing the JSON value as argument to its single parameter. That does not work for builders, because there is no such method that does it both (there are two separate methods, one for receiving a JSON value, and one for constructing the instance).

However, there is a simple workaround in your case without loosing immutability. Just put @JsonCreator on an all-args constructor as follows:

@Value
@AllArgsConstructor(onConstructor_ = {@JsonCreator})
public class Limit {
    @JsonValue
    String value;
}

You can keep the @Builder if you want to, but there is no need to do so.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM