Jackson で Java Enum 列挙型と JSON をシリアライズ・デシリアライズするサンプルコード

## 概要
- Java Enum 列挙型と JSON 内の値を相互変換する
- Jackson の提供する抽象クラス JsonSerializer と JsonDeserializer を使う

## 動作確認環境

- Java 11 (AdoptOpenJDK 11.0.6+10)
- Jackson Databind 2.10.3
- Gradle 6.2.1
- macOS Catalina

## サンプルコード

```java
package com.example.humansex;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

/**
 * ISO 5218 (ヒトの性別の表記のためのコード) を表す列挙型です。
 * 性別コード: 不明=0, 男性=1, 女性=2, 適用不能=9
 */
public enum HumanSex {

  /**
   * 不明=0, 男性=1, 女性=2, 適用不能=9
   */
  NOT_KNOWN(0), MALE(1), FEMALE(2), NOT_APPLICABLE(9);

  private final int code;

  private HumanSex(int code) {
    this.code = code;
  }

  /**
   * 性別コードを返します。
   */
  public int getCode() {
    return code;
  }

  /**
   * 性別コードに合致する HumanSex 列挙定数を返します。
   *
   * @param code 性別コード
   * @return HumanSex 列挙定数
   */
  public static HumanSex codeOf(int code) {
    for (HumanSex value : HumanSex.values()) {
      if (code == value.getCode()) {
        return value;
      }
    }
    return null; // 合致せず
  }

  /**
   * HumanSex オブジェクトを JSON にシリアライズするクラスです。
   */
  public static class Serializer extends JsonSerializer<HumanSex> {

    /**
     * JSON 生成時に HumanSex オブジェクトを性別コード(整数値)に変換します。
     */
    @Override
    public void serialize(HumanSex value, JsonGenerator generator, SerializerProvider serializers) throws IOException {
      generator.writeNumber(value.getCode());
    }
  }

  /**
   * JSON から HumanSex オブジェクトをデシリアライズするクラスです。
   */
  public static class Deserializer extends JsonDeserializer<HumanSex> {

    /**
     * JSON 解析時に性別コード(整数値)を HumanSex オブジェクトに変換します。
     */
    @Override
    public HumanSex deserialize(JsonParser parser, DeserializationContext context) throws IOException {
      return HumanSex.codeOf(parser.getIntValue());
    }
  }
}
```

```java
package com.example;

import com.example.humansex.HumanSex;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.util.ArrayList;
import java.util.List;

public class MyData {

  public List<Person> personList = new ArrayList<>();

  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    for (Person person : personList) {
      buf.append(person + System.lineSeparator());
    }
    return buf.toString();
  }

  public static class Person {

    public String name;

    // Jackson による JSON 生成・解析時に使うクラスをアノテーションで指定する
    @JsonSerialize(using = HumanSex.Serializer.class)
    @JsonDeserialize(using = HumanSex.Deserializer.class)
    public HumanSex sex;

    public Person() {
    }

    public Person(String name, HumanSex sex) {
      this.name = name;
      this.sex = sex;
    }

    @Override
    public String toString() {
      return "name=[" + name + "], sex=[" + sex.toString() + "]";
    }
  }
}
```

```java
package com.example;

import com.example.humansex.HumanSex;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.StringWriter;

public class App {

  public static void main(String[] args) throws IOException {

    // データオブジェクトを生成
    MyData inputData = new MyData();
    inputData.personList.add(new MyData.Person("Peko",   HumanSex.NOT_KNOWN));
    inputData.personList.add(new MyData.Person("Anubis", HumanSex.MALE));
    inputData.personList.add(new MyData.Person("Isis",   HumanSex.FEMALE));
    inputData.personList.add(new MyData.Person("Robot",  HumanSex.NOT_APPLICABLE));

    // Jackson でデータオブジェクトから JSON 文字列を生成
    StringWriter out = new StringWriter();
    new ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(out, inputData);
    String json = out.toString();

    // JSON 文字列を出力
    System.out.println("***** Mydata → JSON *****");
    System.out.println(json);

    // Jackson で JSON 文字列を解析してデータオブジェクトに変換
    MyData outputData = new ObjectMapper().readValue(json, MyData.class);

    // オブジェクトの文字列表現を出力
    System.out.println("***** JSON → Mydata *****");
    System.out.println(outputData);
  }
}
```

### Gradle 設定ファイル

Gradle でビルドと実行をするための設定ファイル build.gradle を用意。

```groovy
build.gradle 
plugins {
  id 'java'
  id 'application'
}

group 'org.example'
version '0.0.1'

sourceCompatibility = 11

repositories {
  mavenCentral()
}

dependencies {
  implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.3'
}

application {
  mainClassName = 'com.example.App'
}
```

## 実行結果

```
***** Mydata → JSON *****
{
  "personList" : [ {
    "name" : "Peko",
    "sex" : 0
  }, {
    "name" : "Anubis",
    "sex" : 1
  }, {
    "name" : "Isis",
    "sex" : 2
  }, {
    "name" : "Robot",
    "sex" : 9
  } ]
}
***** JSON → Mydata *****
name=[Peko], sex=[NOT_KNOWN]
name=[Anubis], sex=[MALE]
name=[Isis], sex=[FEMALE]
name=[Robot], sex=[NOT_APPLICABLE]
```

## 参考資料

- [Home · FasterXML/jackson\-databind Wiki · GitHub](https://github.com/FasterXML/jackson-databind/wiki)
- [Overview \(jackson\-databind 2\.10\.0 API\)](http://fasterxml.github.io/jackson-databind/javadoc/2.10/)
- [Java で文字列や値から Enum 定数を逆引きする \- Qiita](https://qiita.com/niwasawa/items/bb8dcea8e218539b75ad)
- [Java enum 列挙型と JSON を Jackson で相互変換する \- Qiita](https://qiita.com/niwasawa/items/14f1d0551d5c2f0db3e0)