En-05: Enum のシリアライズ・JSON 変換
Enum を JSON や DB に保存・復元する際の実践的なパターンを解説します。ordinal() 保存の危険性と、 固定コード値による安全な永続化方法を理解しましょう。
いつ使うか
- Enum の値を DB に保存・読み込みするとき
- Enum を JSON に変換して API で送受信するとき
- Java の標準シリアライズ(ObjectOutputStream)で Enum を扱うとき
永続化方式の比較
| 方式 | 例 | 安全性 | 備考 |
|---|---|---|---|
ordinal() | 0, 1, 2 | 危険 | 定義順変更で壊れる |
name() | PROCESSING | 注意 | 定数名変更で壊れる |
| 固定コード値 | PROC | 安全 | 推奨。順序変更・リネームに強い |
サンプルコード
Java 8 では fromDbCode() に for ループを使います。Enum は java.io.Serializable を実装しているので、標準シリアライズを使う場合は自動的にシリアライズできます。デシリアライズ後も同一インスタンス(シングルトン)が保証されます。
Jackson を使った JSON 変換(許可ライブラリ)
実務では Jackson を使った JSON 変換が一般的です。@JsonValue と@JsonCreator アノテーションを使うことで、 シリアライズ・デシリアライズ時に固定コード値を使うよう指定できます。
Jackson を使わない場合は、Pure Java でも getDbCode() とfromDbCode() で同等の変換を実装できます。
よくあるミス・注意点
ordinal() を永続化に使ってはいけない
ordinal() は定義順の 0 始まりの整数です。 Enum に新しい定数を途中に挿入したり、定義順を変更したりすると値がずれて、 既存の DB データや JSON の意味が変わってしまいます。 永続化には必ず明示的なコード値か name() を使いましょう。
name() による保存は定数のリネームに弱い
name() は定数名の文字列(例: "PROCESSING")を返します。 ordinal より安全ですが、定数名をリファクタリングで変更すると既存データと一致しなくなります。 長期運用するシステムでは固定コード値の方が安全です。
Jackson のデフォルトは name() で変換する
Jackson は設定なしだと Enum をname()("PROCESSING" など)でシリアライズします。 固定コード値("PROC" など)を使いたい場合は@JsonValue と@JsonCreator を明示的に付けましょう。
Enum の標準シリアライズは readResolve() が不要
通常のクラスをシリアライズしてシングルトンを保つにはreadResolve() が必要ですが、 Enum は JVM レベルでシングルトンが保証されており、デシリアライズ後も同一インスタンスが返ります。
テストする観点
getDbCode()が各定数の期待するコード値("PEND", "PROC" など)を返すことfromDbCode()に各定数のコード値を渡したとき、対応する Enum が返ること(正常系・全件)fromDbCode()に存在しないコード値を渡したときIllegalArgumentExceptionがスローされること(異常系)fromDbCode()に空文字を渡したときIllegalArgumentExceptionがスローされること(境界値)name()が定数名の文字列を返し、valueOf(name())で元の Enum に戻せること- dbCode → fromDbCode → getDbCode の往復で同じコード値が得られること(ラウンドトリップ)