14
Sep 2022
by
佐々木 亜里沙
@JsonCreator
使っているプロジェクトはいくつかあるのですが、時々デシリアライズ部分でエラーが発生し、Lombok とも合わせると問題がどこなのかわからなくなったりするので調べてみました。
かなり消化不良なまとめですが、@JsonCreator
使う時は jackson がよしなにやろうとしているロジックやパターンが複雑なので、明示的にこちらからどういう方法でデシリアライズしてねという指定をしておいた方が、デシリアライズ時にエラーに悩むことが少なくなりそうという感じでした。
最新の jackson は現時点で 2.13.x で、今後 3.x が出てきますが、2.12.6 バージョンで調査したものになります。
調査して分かったこと
@JsonCreator
には4つのモードがある。デフォルトはJsonCreator.Mode.DEFAULT
DEFAULT
- ヒューリスティックにコードを解析し、渡される JSON のプロパティ名やゲッター・セッターなどから DELEGATING か PROPERTIESかを推測する
DELEGATING
- このモードを指定したメソッドのロジックにてデシリアライズされる
PROPERTIES
- クラスのフィールド名や型情報を元にデシリアライズする
DISABLED
- mix-inなどを使う場合に指定する
引数が一つの場合と引数が複数の場合のコンストラクタではデシリアライズの仕方に違いがある。
@JsonCreator
の定義がない場合、デフォルトコンストラクタが呼ばれる@NoArgsConstructor(force = true, onConstructor = @__(@JsonCreator)
の場合はこれが適用され、モードの推測がされない分、不具合が起こりにくい?
@JsonCreator
があり、引数一つの場合、ヒューリスティックに推測されるが、プロパティベースで解決できない場合もあるため、モードを指定しておいた方が良い (@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
)- たとえば String -> UUID へ変換する必要がある場合はスタティックファクトリメソッドを作成し、 @JsonCreator をアノテートするが、 mode が指定されていないとこのメソッドの内容で処理されない場合がある
@Embeddable
@Value
@AllArgsConstructor(staticName = "of")
public static class FooId implements Identifier, Serializable {
private final UUID id;
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static FooId from(String id) {
return FooId.of(UUID.fromString(id));
}
...
}
@JsonCreator
があり、引数複数の場合 は、基本的にはプロパティベースで処理される。@JsonProperty
の指定がない場合は setter がプロパティの解釈に必要なため定義されている必要がある。@JsonCreator
は複数定義されていても問題ないが、どれか一つだけが採用される。ルールはドキュメント化されていないが、モードでDELEGATING
をつけたものはPROPERTIES
がついている@JsonCreator
よりは優先される挙動をしていた。どの Creator がどのようなオーダーで利用されるかは確認中(不具合も含め、かなり複雑)前述の
@ConstructorProperties
はPROPERTIES
モードと同様、定義されているプロパティから自動的にコンストラクタを作る。推測できない場合はDELEGATING
となる。
推奨方法
@JsonCreator
を利用する場合はモードの指定と@JsonProperty
の定義をしておいた方がベター- 引数を多く持つコンストラクタで
@JsonProperty
の定義を書くのが煩雑になる場合は@JsonPOJOBuilder
をもつ@JsonDeserialize
を記述するともう少しシンプルになる (https://www.baeldung.com/jackson-deserialize-immutable-objects, https://ik.am/entries/483 が参考になります)