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 だらけのオブジェクトになります。
GO TO FULL VERSION