web-dev-qa-db-ja.com

Lombok 1.18.0とJackson 2.9.6が一緒に機能しない

更新後にデシリアライゼーションが失敗します。

マイクロサービスをSpring 1.5.10.RELEASEからSpring 2.0.3.RELEASEに更新し、lombok1.16.14から1.18.0およびjackson-datatype-jsr310から2.9.4に更新しました] _から2.9.6へ。

JSON文字列-

{"heading":"Validation failed","detail":"field must not be null"}

クラス -

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

   private final String heading;
   private final String detail;
   private String type;
}

メソッド呼び出し-

ErrorDetail errorDetail = asObject(jsonString, ErrorDetail.class);

デシリアライズに使用されるメソッド-

import com.fasterxml.jackson.databind.ObjectMapper;
// more imports and class defination.

private static <T> T asObject(final String str, Class<T> clazz) {
    try {
        return new ObjectMapper().readValue(str, clazz);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

エラー-

Java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.foo.bar.ErrorDetail` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"heading":"Validation failed","detail":"field must not be null"}"; line: 1, column: 2]
11
JHS

Lombokは、バージョン1.16.20のコンストラクターで@ConstructorPropertiesの生成を停止しました(モジュールを使用するアプリケーションがJava 9+)を壊す可能性があるため、 changelog を参照)。コンストラクターのパラメーターの名前が含まれています(クラスのコンパイル時に削除されるため、実行時にパラメーター名を引き続き取得できるようにするための回避策です。デフォルトではアノテーションが生成されないため、ジャクソンはフィールド名をマッピングできませんコンストラクターのパラメーター。

解決策1:@NoArgsConstructorおよび@Setterを使用しますが、不変性が失われます(それが重要な場合)。

更新:@NoArgsConstructor@Getter@Setterなし)だけでも動作する場合があります( INFER_PROPERTY_MUTATORS=true =)。このようにして、少なくとも通常の(反射しない)コードからクラスを不変に保つことができます。

解決策2:注釈を再度生成するようにlombokを設定します lombok.configファイルを使用lombok.anyConstructor.addConstructorProperties = trueを含みます。 (モジュールを使用している場合は、Java.desktopがモジュールパスにあることを確認してください。)

解決策3:この質問に対して here または @ Randakar answer で説明されているように、Lombokの@Builderと組み合わせてJacksonのビルダーサポートを使用します。

25
Jan Rieke

Jacksonとlombokを適切に連携させる最善の方法は、DTOを常に不変にし、jacksonにBuilderを使用してオブジェクトにデシリアライズするように指示することです。

不変オブジェクトは、フィールドをin situで変更できない場合、コンパイラーがより積極的な最適化を行うことができるという単純な理由から、良いアイデアです。

これを行うには、JsonDeserializeとJsonPojoBuilderの2つのアノテーションが必要です。

例:

@Builder
@Value // instead of @Data
@RequiredArgsConstructor
@NonNull // Best practice, see below.
@JsonDeserialize(builder = ErrorDetail.ErrorDetailBuilder.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

   private final String heading;

   // Set defaults if fields can be missing, like this:
   @Builder.Default
   private final String detail = "default detail";

   // Example of how to do optional fields, you will need to configure
   // your object mapper to support that and include the JDK 8 module in your dependencies..
   @Builder.Default
   private Optional<String> type = Optional.empty()

   @JsonPOJOBuilder(withPrefix = "")
   public static final class ErrorDetailBuilder {
   }
}
5
Randakar

Finalフィールドを持つクラスを逆シリアル化したい場合。そのため、逆シリアル化する最終フィールドを含むコンストラクタを宣言する必要があります。

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

private final String heading;
private final String detail;
private String type;

@JsonCreator
public ErrorDetail(@JsonProperty("heading") String heading, @JsonProperty("detail") String detail) {
    this.heading = heading;
    this.detail = detail;
}
}

また、マッパーでデシリアライズするときにMapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORSこのプロパティを設定する必要がありますfalse

private static <T> T asObject(final String str, Class<T> clazz) {
    try {
        return new ObjectMapper().configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS,false).readValue(str, clazz);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
1
sajib

私の意見では、@Dataアノテーションの使用は悪いアプローチです。 @Data@Getting@Setter@EqualsAndHashcodeなどに変更してください。

助けになるならここに書いてください。

更新

@Data create @RequiredArgsConstructorをお勧めします。これは、最終フィールドを持ち、private String typeを持たないコンストラクターです。

0
lord_hokage