java-recipes

ホーム Record › Rec-02

Rec-02: レコードのシリアライズ・JSON 変換

Java の record を JSON に変換・復元する実践的なパターンを解説します。 Pure Java による手動変換と、実務でよく使われるJacksonライブラリを使う方法の両方を紹介します。

record のシリアライズとは

「シリアライズ」とは、オブジェクトをバイト列や文字列(JSON など)に変換して保存・転送できるようにすることです。 API のレスポンスを JSON として返す場面や、セッション情報をファイルに保存する場面でよく使います。

主な変換パターン

  • JSON 変換(テキスト形式): Web API のレスポンスや設定ファイルへの書き込みに使います
  • バイナリシリアライズ(バイト形式): Java 標準の ObjectOutputStream / ObjectInputStream を使います。Serializable の実装が必要です

Java バージョンごとの違い

  • Java 8: record が使えないため、不変クラスで同等の実装を手書きします
  • Java 17: record を使って DTO を1行で定義できます。バイナリシリアライズには Serializable の明示的な implements が必要です
  • Java 21: sealed interface と record を組み合わせ、switch パターンマッチングで型安全な JSON 変換ができます

サンプルコード

RecordSerializeSample.java
import java.io.*;

public class RecordSerializeSample {

    // Java 8: Record の代わりに不変クラスで実装
    static class UserDto implements Serializable {
        private static final long serialVersionUID = 1L;

        private final String id;
        private final String name;
        private final int age;

        UserDto(String id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }

        String getId() { return id; }
        String getName() { return name; }
        int getAge() { return age; }

        @Override
        public String toString() {
            return "UserDto{id='" + id + "', name='" + name + "', age=" + age + "}";
        }
    }

    // 手動 JSON 変換(Pure Java / Java 8)
    static String toJson(UserDto user) {
        return "{\"id\":\"" + user.getId() + "\","
                + "\"name\":\"" + user.getName() + "\","
                + "\"age\":" + user.getAge() + "}";
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        UserDto user = new UserDto("U001", "田中太郎", 30);
        System.out.println("元オブジェクト: " + user);

        // 手動 JSON 変換
        String json = toJson(user);
        System.out.println("JSON: " + json);

        // バイナリシリアライズ
        byte[] bytes;
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(user);
            bytes = baos.toByteArray();
        }

        try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            UserDto loaded = (UserDto) ois.readObject();
            System.out.println("復元: " + loaded);
        }
    }
}

Java 8 では record が使えないため、不変クラス(final フィールド + final クラス)で同等の実装を行います。JSON 変換は文字列結合で手動対応します。実務では Jackson などのライブラリを使うことが一般的です。

よくあるミス・注意点

⚠️ record は Serializable を implements しないとバイナリシリアライズできない

record は自動的に Serializable を実装しません。ObjectOutputStream でシリアライズしようとするとNotSerializableException が発生します。 バイナリシリアライズが必要な場合は record UserDto(...) implements Serializable のように明示的に追加してください。

⚠️ 手動 JSON 変換は特殊文字のエスケープ処理が必要

文字列結合で JSON を手動生成する場合、値に "(ダブルクォート)や\(バックスラッシュ)が含まれると不正な JSON になります。 実務では Jackson や Gson などのライブラリを使うと、エスケープ処理を自動で行ってくれます。 Pure Java の手動変換は学習用・シンプルな DTO 限定と考えてください。

⚠️ record のアクセサメソッドは get なし形式(Java 17+)

Java 17+ の record では user.getId() ではなくuser.id() がアクセサ名です。 Jackson でデフォルト設定のまま record を JSON 変換しようとすると、get なしのアクセサを認識できずプロパティが空になることがあります。 Jackson 2.12 以降は record に対応していますが、古いバージョンでは @JsonProperty アノテーションが必要です。

テストする観点

  • 手動 JSON 変換で toJson() が正しい JSON 文字列({"id":"U001","name":"田中太郎","age":30})を返すこと
  • バイナリシリアライズ → デシリアライズを経ても、元のオブジェクトと equals() が成立すること
  • Serializable を implements していない record を ObjectOutputStream に渡すと NotSerializableException が発生すること
  • Java 21 の sealed record で全バリアント(Circle / Rectangle / Triangle)が JSON 変換されること
  • 値に特殊文字(ダブルクォートなど)が含まれる場合の変換結果が正しいこと(境界値)

GitHub でソースコードを見る →