[java tips] lombok @Builderを使ったクラスでもjson fixtureを使いたい

# 前提

* Java
* Spring boot
* Jacksonを利用してobject <-> json変換
* junitで単体テストを書く
* lombokを利用している

# 概要

Javaでunittestを書くとき、テストで使うデータを用意するのはわりとめんどくさい。

```java
var hoge = new Hoge("hogeId", "hogeName", new Fuga("fugaId", "fugaName", ...));
````

というわけで、一般的には外部にjsonファイルを用意し、これを読み込ませてテストに使う。

* hoge.json

```json
{
   "id": "hogeId",
   "name": "hogeName",
   "fuga": {
      "id": "fugaId",
      "name": "fugaName",
      ...
   }
}
```

しかし、一部のオブジェクト（たとえばドメインモデル）は「不変オブジェクト（immutable object）」にしないといけない。そういうコーディングルールはよくある。

```java
@Builder
@Getter
public class Hoge {
   private final String id;
   private final String name;
   private final Fuga fuga;
}
```

これをObjectMapperを使って読み込むことができるか？

# 実装

もちろん可能。

```java
@JsonDeserialize(builder=Hoge.HogeBuilder.class)
@Builder
@Getter
public class Hoge {
   private final String id;
   private final String name;
   private final Fuga fuga;

   @JsonPOJOBuilder(withPrefix="")
   public static class HogeBuilder {}
}
```

これで、

```java
   @Test
   public void testHoge() throws IOException {
      var fixture = getClass().getResource("./hoge.json");
      var hoge = mapper.readValue(fixture, Hoge.class);

      ...
   }
```

いつも通りjson fixtureを使ったテストができる。

# 参照

https://stackoverflow.com/questions/39381474/cant-make-jackson-and-lombok-work-together

https://www.thecuriousdev.org/lombok-builder-with-jackson/

テストのためにモデルにアノテーションを付与することがコーディングルール上許されるか？の問題は残るが・・・。


# おおっと（余談）

話はこれで終わらないことが判明。

```java
@Builder
@Getter
@JsonDeserialize(builder=Fuga.FugaBuilder.class)
public class Fuga {
   private final String id;
   private final String name;
   private final Hoge hoge;

   @JsonPOJOBuilder(withPrefix="")
   public static class FugaBuilder {}
}

@Builder
@Getter
// value object
public class Hoge {
   private final String hoge;
}
```

こういった「文字列を含むプリミティブ型一属性しかもたないValueObjectは、ObjectMapperは特別扱いする」ことが分かった。
こういったimmutableな一属性モデルを、ObjectMapperはデフォルトでDDDのvalue objectとして扱う。

```json
{
   "id": "fugaId",
   "name": "fugafuga",
   "hoge": {
      "hoge": "hogehoge"
   }
}
```
これはエラーになり、

```json
{
   "id": "fugaId",
   "name": "fugafuga",
   "hoge": "hogehoge"
}
```
これだと通る。こういうjson階層を無視するような動きをjacksonはDelegatingと言うらしい。つまり

```java
@Builder
@Getter
@JsonDeserialize(builder=Fuga.FugaBuilder.class)
public class Fuga {
   private final String id;
   private final String name;
   private final Hoge hoge;

   @JsonPOJOBuilder(withPrefix="")
   public static class FugaBuilder {}
}

@Builder
@Getter
// value objectチックだが、primitiveじゃない
public class Hoge {
   private final Hogehoge hoge;
}

@Builder
@Getter
public class Hogehoge {
   private final String id;
   private final String name;
}

```
こんなモデルは

```json
{
   "id": "fugaId",
   "name": "fugafuga",
   "hoge": {
      "hoge": {
         "id": "hogeId",
         "name": "hogeId"
      }
   }
}
```
こんなjsonになるが、delegatingしたいなら

```java
@Builder
@Getter
@JsonDeserialize(builder=Fuga.FugaBuilder.class)
public class Fuga {
   private final String id;
   private final String name;
   private final Hoge hoge;

   @JsonPOJOBuilder(withPrefix="")
   public static class FugaBuilder {}
}

@Builder
@Getter
public class Hoge {
   private final Hogehoge hoge;

   // Delegatingを付与できるのが@JsonCreator以外見つからず。
   @JsonCreator(mode=JsonCreator.Mode.Delegating)
   public static Hoge newHoge(Hogehoge hogehoge){
      return Hoge.builder().hoge(hogehoge).build();
   }
}

@Builder
@Getter
@JsonDeserialize(builder=Hogehoge.HogehogeBuilder.class)
public class Hogehoge {
   private final String id;
   private final String name;

   @JsonPOJOBuilder(withPrefix="")
   public static class HogehogeBuilder {}
}
```

こんなコードを書いてあげるとjson階層が繰り上がって

```json
{
   "id": "fugaId",
   "name": "fugafuga",
   "hoge": {
      "id": "hogeId",
      "name": "hogeId"
   }
}
```

こうなる。jacksonはまだまだ深い・・・・