CodeGym /コース /JAVA 25 SELF /Gson — シリアライズとデシリアライズ、設定

Gson — シリアライズとデシリアライズ、設定

JAVA 25 SELF
レベル 46, レッスン 2
使用可能

1. Gson 入門

私たちはすでに Jackson に触れ、Java で JSON を扱う事実上の標準とされる理由を見てきました。一方で、特に Android の世界で非常に人気のある別のライブラリが Gson です。Gson は Google で、Java オブジェクトと JSON のシリアライズ/デシリアライズを手軽に行うための軽量なソリューションとして作られました。設定なしで始められる敷居の低さが評価されており、多くのタスクは文字通り「すぐに使える」形で解決できます。

Gson のもう一つの利点は軽量さです。ライブラリ自体が小さく、依存関係も多くありません。そのためアプリサイズが重要になる場面、例えばモバイル端末でよく使われます。Gson は Android プロジェクトでは事実上の標準となっており、コンパクトさとシンプルさが大きな武器になっています。

ちなみに、Gson は Google JSON の略です。コミュニティでは冗談で Genius’ Son(「天才の息子」)と言われることもありますが、もちろん非公式で、単なる語呂合わせです。

Gson をプロジェクトに追加する

Maven または Gradle を使っている場合は、依存関係を追加するだけです(バージョンは異なる場合があります)。

Maven:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

Gradle:

implementation 'com.google.code.gson:gson:2.10.1'

まだビルドツールの学習前であれば、まずは Gson の公式ページ から jar ファイルをダウンロードし、プロジェクトに追加しても構いません。

2. 基本操作: シリアライズとデシリアライズ

簡単なクラスを例に、Gson でオブジェクトをシリアライズ/デシリアライズする方法を見てみましょう。

例: User クラス

// 例用のクラス
public class User {
    private String name;
    private int age;
    private boolean active;

    // コンストラクター
    public User(String name, int age, boolean active) {
        this.name = name;
        this.age = age;
        this.active = active;
    }

    // getter と setter(必要に応じて Gson が使用)
    public String getName() { return name; }
    public int getAge() { return age; }
    public boolean isActive() { return active; }
}

シリアライズ: オブジェクト → JSON

import com.google.gson.Gson;

public class GsonExample {
    public static void main(String[] args) {
        User user = new User("Alice", 25, true);

        Gson gson = new Gson();
        String json = gson.toJson(user);

        System.out.println(json);
        // {"name":"Alice","age":25,"active":true}
    }
}

注意: フィールドはクラスのフィールド名のままシリアライズされます!

デシリアライズ: JSON → オブジェクト

public class GsonExample {
    public static void main(String[] args) {
        String json = "{\"name\":\"Bob\",\"age\":30,\"active\":false}";

        Gson gson = new Gson();
        User user = gson.fromJson(json, User.class);

        System.out.println(user.getName()); // Bob
        System.out.println(user.getAge());  // 30
        System.out.println(user.isActive());// false
    }
}

オブジェクトのリストを扱う

Gson はコレクションの扱いが Jackson より少しだけ複雑ですが、対応可能です。

import java.util.List;
import java.util.Arrays;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;

public class GsonListExample {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Alice", 25, true),
            new User("Bob", 30, false)
        );

        Gson gson = new Gson();
        String json = gson.toJson(users);
        System.out.println(json);
        // [{"name":"Alice","age":25,"active":true},{"name":"Bob","age":30,"active":false}]

        // リストのデシリアライズ
        Type userListType = new TypeToken<List<User>>(){}.getType();
        List<User> users2 = gson.fromJson(json, userListType);
        System.out.println(users2.get(0).getName()); // Alice
    }
}

重要な注意点: コレクションをデシリアライズする場合は TypeToken<> を使用しましょう!

3. Gson の設定: GsonBuilder

Gson は GsonBuilder によって柔軟に設定できます。これにより、pretty printing、null のシリアライズ、日付のフォーマットなどを有効にできます。

例: pretty printing と null のシリアライズ

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class GsonBuilderExample {
    public static void main(String[] args) {
        User user = new User("Charlie", 0, false);

        Gson gson = new GsonBuilder()
            .setPrettyPrinting()        // 見やすい整形出力(インデント付き)
            .serializeNulls()           // null フィールドもシリアライズ
            .create();

        String json = gson.toJson(user);
        System.out.println(json);
        /*
        {
          "name": "Charlie",
          "age": 0,
          "active": false
        }
        */
    }
}

日付のフォーマット

Date 型のフィールドがある場合、デフォルトでは Gson は独自の形式でシリアライズします。任意のフォーマットを指定できます。

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Date;

public class DateExample {
    private String event;
    private Date date;

    public DateExample(String event, Date date) {
        this.event = event;
        this.date = date;
    }
}

public class Main {
    public static void main(String[] args) {
        DateExample meeting = new DateExample("Team Meeting", new Date());

        Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd HH:mm:ss")
            .create();

        String json = gson.toJson(meeting);
        System.out.println(json);
        // {"event":"Team Meeting","date":"2024-06-10 13:45:23"}
    }
}

4. Gson のアノテーション: シリアライズの制御

Gson は、シリアライズ/デシリアライズをより細かく制御するためのアノテーションをサポートしています。

@SerializedName — フィールド名の変更

JSON 上のフィールド名を別名にしたい場合は、@SerializedName を使います。

import com.google.gson.annotations.SerializedName;

public class User {
    @SerializedName("full_name")
    private String name;
    private int age;
    private boolean active;

    public User(String name, int age, boolean active) {
        this.name = name;
        this.age = age;
        this.active = active;
    }
}

これでシリアライズ時のフィールド名は full_name になります。

User user = new User("Diana", 28, true);
String json = new Gson().toJson(user);
// {"full_name":"Diana","age":28,"active":true}

@Expose — 指定したフィールドのみをシリアライズ

特定のフィールドだけをシリアライズしたい場合は、@Expose を使い、Gson を設定します。

import com.google.gson.annotations.Expose;

public class User {
    @Expose
    private String name;

    @Expose
    private int age;

    private boolean active; // シリアライズされない

    public User(String name, int age, boolean active) {
        this.name = name;
        this.age = age;
        this.active = active;
    }
}

@Expose に対応した Gson を作成します。

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

Gson gson = new GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .create();

User user = new User("Eve", 21, false);
String json = gson.toJson(user);
// {"name":"Eve","age":21}

@Since/@Until — バージョンによる条件付きシリアライズ
@Since と @Until を使うと、特定のバージョン向けにのみフィールドをシリアライズできます(実務で使う機会は多くありませんが、知っておくと便利です)。

5. Gson の特徴と制約

入れ子オブジェクトの扱い

Gson は入れ子になったオブジェクトも問題なく扱えます。

public class Profile {
    private User user;
    private String bio;

    public Profile(User user, String bio) {
        this.user = user;
        this.bio = bio;
    }
}

Profile profile = new Profile(new User("Frank", 27, true), "Java developer");
String json = new Gson().toJson(profile);
// {"user":{"name":"Frank","age":27,"active":true},"bio":"Java developer"}

コレクションの扱い

コレクション(List、Map)のシリアライズは問題ありませんが、デシリアライズには TypeToken を使いましょう(前述)。

Jackson と比べたときの Gson の制約

  • Java の record クラスを未サポート(最近のバージョンまで)
  • 新しい日時 API のサポートが限定的(例: LocalDate、LocalDateTime — カスタムアダプターが必要)
  • Jackson のアノテーションは扱えない
  • 複雑なポリモーフィック構造を「すぐに使える」形ではサポートしていない
  • 双方向参照(循環)の自動サポートなし

カスタムアダプター (TypeAdapter)

標準機能で足りない場合は、複雑な型のための独自アダプターを実装できます。

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;

public class BooleanAsIntAdapter extends TypeAdapter<Boolean> {
    @Override
    public void write(JsonWriter out, Boolean value) throws IOException {
        out.value(value ? 1 : 0);
    }

    @Override
    public Boolean read(JsonReader in) throws IOException {
        return in.nextInt() == 1;
    }
}

// 使い方:
Gson gson = new GsonBuilder()
    .registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
    .create();

6. 実践: 設定付きでのシリアライズ/デシリアライズ

学習用アプリを発展させ、ユーザー一覧を JSON 形式で保存・読み込みしてみましょう。

アノテーション付き User クラス

import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;

public class User {
    @Expose
    @SerializedName("full_name")
    private String name;

    @Expose
    private int age;

    private boolean active; // シリアライズされない

    public User(String name, int age, boolean active) {
        this.name = name;
        this.age = age;
        this.active = active;
    }

    // getter・setter など...
}

ユーザー一覧を JSON に保存する

import java.util.List;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class SaveUsers {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Ivan", 23, true),
            new User("Olga", 19, false)
        );

        Gson gson = new GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .setPrettyPrinting()
            .create();

        String json = gson.toJson(users);
        System.out.println(json);
        /*
        [
          {
            "full_name": "Ivan",
            "age": 23
          },
          {
            "full_name": "Olga",
            "age": 19
          }
        ]
        */
    }
}

JSON からユーザー一覧を読み込む

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;

public class LoadUsers {
    public static void main(String[] args) {
        String json = "[{\"full_name\":\"Ivan\",\"age\":23},{\"full_name\":\"Olga\",\"age\":19}]";

        Gson gson = new GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .create();

        Type userListType = new TypeToken<List<User>>(){}.getType();
        List<User> users = gson.fromJson(json, userListType);

        for (User user : users) {
            System.out.println(user.getName() + " (" + user.getAge() + ")");
        }
        // Ivan (23)
        // Olga (19)
    }
}

7. Gson と Jackson の比較

項目 Gson Jackson
使いやすさ +++++(とても簡単) +++(やや複雑)
ライブラリのサイズ 小さい 大きめ
速度 高速だがやや遅め 非常に高速
柔軟性 中程度 高い(設定が豊富)
アノテーション対応 独自(@SerializedName) 独自(@JsonProperty など)
新しい型の対応 限定的 優れている(Java 8+、record)
Android 対応 優れている 良いが重め
日付の扱い アダプター経由のみ 標準で対応
ポリモーフィズム 限定的 柔軟に設定可能

8. Gson でよくあるミス

エラー No.1: コレクションに TypeToken を使っていない。
リストやマップをデシリアライズする場合は必ず TypeToken<> を使いましょう。使わないと、不審なエラーや空のコレクションになることがあります。

エラー No.2: 引数なしコンストラクターがない。
Gson はデフォルトコンストラクターなしでも動くことはありますが、複雑なオブジェクトのデシリアライズでは、コンストラクターがないと問題になることがあります。デシリアライズを行う予定があるなら、引数なしコンストラクターを用意しておくのが無難です。

エラー No.3: フィールド名が一致しない。
JSON 側のフィールドが "full_name" で、クラス側が "name" の場合、@SerializedName("full_name") を付けないと紐付かず、値は null になります。

エラー No.4: private フィールドの問題。
Gson は private フィールドもシリアライズできますが、フィールドが private のみで getter/setter もないと、デシリアライズ時に問題が起きることがあります。getter と setter を用意するのが良いでしょう。

エラー No.5: 日付の扱い。
デフォルトでは Gson は Date を使いにくい形式でシリアライズします。LocalDate、LocalDateTime などの新しい型は、カスタムアダプターがないとシリアライズでエラーになります。

エラー No.6: @Expose を使わずに excludeFieldsWithoutExposeAnnotation() を有効にしている。
excludeFieldsWithoutExposeAnnotation() を有効化しているのにフィールドへ @Expose を付けないと、それらはシリアライズ/デシリアライズされません。結果として空の JSON や、null だらけのオブジェクトになります。

1
タスク
JAVA 25 SELF, レベル 46, レッスン 2
ロック未解除
外部システムとの学生データ同期 🎓
外部システムとの学生データ同期 🎓
1
タスク
JAVA 25 SELF, レベル 46, レッスン 2
ロック未解除
ソーシャルネットワークでの公開プロフィール作成 🌐
ソーシャルネットワークでの公開プロフィール作成 🌐
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION