Tagbangers Blog

jackson の @JsonCreator を使ったデシリアライズについて調べたこと

@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 がどのようなオーダーで利用されるかは確認中(不具合も含め、かなり複雑)

  • 前述の @ConstructorPropertiesPROPERTIES モードと同様、定義されているプロパティから自動的にコンストラクタを作る。推測できない場合は DELEGATING となる。


推奨方法