Gson完全ガイド:JavaとJSONを自在に操るための詳細解説

この記事から得られる知識

この記事を読むことで、あなたは以下の知識を体系的に習得できます。

  • Gsonライブラリの基本的な概念とプロジェクトへの導入方法
  • JavaオブジェクトとJSON文字列間の基本的な変換(シリアライズ・デシリアライズ)
  • ネストしたオブジェクト、リスト、マップなどの複雑なデータ構造の取り扱い
  • GsonBuilderを使用したGsonインスタンスの高度なカスタマイズ方法(Pretty Printing、nullの処理など)
  • @SerializedNameアノテーションによるJavaフィールド名とJSONキー名のマッピング
  • ExclusionStrategy@Exposeを用いた、特定のフィールドを変換対象から除外するテクニック
  • ジェネリクス型を安全に扱うためのTypeTokenの利用方法
  • Java 8以降の日時APIなど、標準で対応していない型を扱うためのカスタムシリアライザ・デシリアライザの実装方法

はじめに:Gsonとは何か?

Gson(ジーソン)は、Googleによって開発されたオープンソースのJavaライブラリです。その主な目的は、JavaオブジェクトをJSON(JavaScript Object Notation)形式の文字列に変換(シリアライズ)したり、その逆のJSON文字列を等価なJavaオブジェクトに変換(デシリアライズ)したりすることです。

現代の多くのWebアプリケーションやサービスでは、サーバーとクライアント間、あるいはサービス間のデータ交換形式としてJSONが広く採用されています。Javaで開発を行う上で、このJSONデータを効率的かつ安全に扱うことは非常に重要です。Gsonは、そのシンプルで直感的なAPIと高い信頼性から、多くのJava開発者に選ばれ続けているライブラリです。

Gsonの大きな特徴の一つは、変換対象となるJavaクラスに特別なアノテーションを付与する必要が基本的にはない点です。既存の改変できないクラスであっても、容易にJSONとの相互変換が可能です。また、Javaのジェネリクスを広範囲にサポートしているため、複雑なデータ構造も柔軟に扱うことができます。


第1章: Gsonのセットアップと基本的な使い方

プロジェクトへの導入

Gsonを利用するためには、まずプロジェクトの依存関係にGsonライブラリを追加する必要があります。ビルドツールとしてMavenまたはGradleを使用するのが一般的です。

Mavenの場合 (`pom.xml`)

<dependencies>タグ内に以下の<dependency>を追加します。バージョンは執筆時点で比較的新しいものを記載していますが、Maven Centralで最新のバージョンを確認することをお勧めします。

<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.13.1</version>
</dependency>

Gradleの場合 (`build.gradle`)

dependenciesブロックに以下の行を追加します。

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation 'com.google.code.gson:gson:2.13.1'

Gsonインスタンスの生成

Gsonの機能を利用するには、まずcom.google.gson.Gsonクラスのインスタンスを生成します。最も簡単な方法は、デフォルトコンストラクタを呼び出すことです。

import com.google.gson.Gson;

public class Main {
    public static void main(String[] args) {
        // 最も基本的なGsonインスタンスの生成
        Gson gson = new Gson();
        System.out.println("Gsonインスタンスが生成されました。");
    }
}

Gsonインスタンスの再利用

Gsonオブジェクトは、JSON操作中に内部状態を保持しません。つまり、スレッドセーフであり、複数のシリアライズ・デシリアライズ操作で同じインスタンスを安全に再利用できます。パフォーマンス向上の観点からも、操作のたびに新しいインスタンスを生成するのではなく、一度生成したインスタンスをアプリケーション全体で共有・再利用することが推奨されます。


第2章: JavaオブジェクトからJSONへ (シリアライズ)

JavaオブジェクトをJSON文字列に変換するプロセスをシリアライズと呼びます。Gsonでは、toJson()メソッドを使用します。

基本的なデータ型のシリアライズ

プリミティブ型やそのラッパークラス、文字列、配列などを簡単にJSONに変換できます。

import com.google.gson.Gson;

public class SerializationBasic {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // プリミティブ型
        System.out.println("int: " + gson.toJson(100));            // 出力: 100
        System.out.println("String: " + gson.toJson("Hello, Gson!")); // 出力: "Hello, Gson!"
        System.out.println("boolean: " + gson.toJson(true));        // 出力: true
        System.out.println("double: " + gson.toJson(99.99));       // 出力: 99.99

        // 配列
        int[] numbers = {1, 2, 3, 4, 5};
        System.out.println("Array: " + gson.toJson(numbers));     // 出力:

        String[] fruits = {"Apple", "Banana", "Cherry"};
        System.out.println("String Array: " + gson.toJson(fruits)); // 出力: ["Apple","Banana","Cherry"]
    }
}

POJO (Plain Old Java Object) のシリアライズ

実際のアプリケーション開発では、独自のクラス(POJO)をJSONに変換する場面がほとんどです。以下のようなUserクラスを例に見てみましょう。

// 変換対象のUserクラス
public class User {
    private String name;
    private int age;
    private String email;
    private boolean isDeveloper;
    private Address address; // ネストしたオブジェクト
    private String[] skills; // 配列

    public User(String name, int age, String email, boolean isDeveloper, Address address, String[] skills) {
        this.name = name;
        this.age = age;
        this.email = email;
        this.isDeveloper = isDeveloper;
        this.address = address;
        this.skills = skills;
    }
    // getter/setterは省略
}

// Userクラスが持つAddressクラス
public class Address {
    private String country;
    private String city;

    public Address(String country, String city) {
        this.country = country;
        this.city = city;
    }
    // getter/setterは省略
}

このUserオブジェクトをシリアライズするコードは以下のようになります。

import com.google.gson.Gson;

public class SerializeObject {
    public static void main(String[] args) {
        Gson gson = new Gson();

        Address address = new Address("Japan", "Tokyo");
        String[] skills = {"Java", "SQL", "Cloud"};
        User user = new User("Taro Yamada", 30, "taro.yamada@example.com", true, address, skills);

        String jsonOutput = gson.toJson(user);
        System.out.println(jsonOutput);
    }
}

実行結果 (JSON):

{"name":"Taro Yamada","age":30,"email":"taro.yamada@example.com","isDeveloper":true,"address":{"country":"Japan","city":"Tokyo"},"skills":["Java","SQL","Cloud"]}

ご覧の通り、ネストされたオブジェクトや配列を含む複雑なオブジェクトも、toJson()メソッドを一度呼び出すだけで、期待通りのJSON文字列に変換されていることがわかります。


第3章: JSONからJavaオブジェクトへ (デシリアライズ)

JSON文字列をJavaオブジェクトに変換するプロセスをデシリアライズと呼びます。Gsonでは、fromJson()メソッドを使用します。

このメソッドは2つの引数を取ります。第一引数にJSON文字列、第二引数に変換先のJavaクラスのClassオブジェクトを指定します。

単純なJSONのデシリアライズ

前章で作成したUserクラスと対応するJSON文字列を使って、デシリアライズを試してみましょう。

import com.google.gson.Gson;

public class DeserializeObject {
    public static void main(String[] args) {
        Gson gson = new Gson();

        String jsonInput = "{\"name\":\"Hanako Tanaka\",\"age\":25,\"email\":\"hanako.tanaka@example.com\",\"isDeveloper\":false,\"address\":{\"country\":\"USA\",\"city\":\"New York\"},\"skills\":[\"Python\",\"Marketing\"]}";

        // JSONからUserオブジェクトに変換
        User user = gson.fromJson(jsonInput, User.class);

        // 結果の確認
        System.out.println("Name: " + user.getName());
        System.out.println("Age: " + user.getAge());
        System.out.println("City: " + user.getAddress().getCity());
        System.out.println("First Skill: " + user.getSkills());
    }
}

JSONのキー名とJavaクラスのフィールド名が一致していれば、Gsonが自動的にマッピングを行い、オブジェクトを正しく復元してくれます。

JSON配列からJavaのListへ

JSONがオブジェクトの配列である場合、JavaのListにデシリアライズしたいケースが頻繁にあります。しかし、ここにはJavaの型消去(Type Erasure)という特性による注意点があります。

単純にgson.fromJson(jsonArray, List.class)と記述しても、Gsonはリストの要素がどの型(UserなのかAddressなのか)であるかを実行時に知ることができません。この問題を解決するために、GsonはTypeTokenというクラスを提供しています。

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

public class DeserializeList {
    public static void main(String[] args) {
        Gson gson = new Gson();

        String jsonArrayInput = "[{\"name\":\"Taro Yamada\",\"age\":30},{\"name\":\"Hanako Tanaka\",\"age\":25}]";

        // ★ TypeTokenを使ってList<User>の型情報を取得
        Type userListType = new TypeToken<List<User>>(){}.getType();

        // JSON配列をList<User>に変換
        List<User> userList = gson.fromJson(jsonArrayInput, userListType);

        // 結果の確認
        for (User user : userList) {
            System.out.println("User: " + user.getName() + ", Age: " + user.getAge());
        }
    }
}

ポイント: new TypeToken<List<User>>(){}.getType()という少し奇妙な構文は、匿名内部クラスを作成することで、ジェネリクスの型情報(この場合はList<User>)を実行時まで保持するためのテクニックです。これにより、Gsonは正しくList<User>を生成できます。


第4章: Gsonを使いこなすための応用テクニック

Gsonの真価は、その高いカスタマイズ性にあります。GsonBuilderクラスを使うことで、様々な挙動を細かく制御できます。

GsonBuilderによるインスタンスのカスタマイズ

new Gson()の代わりにnew GsonBuilder().create()を使うことで、カスタマイズされたGsonインスタンスを生成できます。

メソッド 説明
setPrettyPrinting() JSONの出力をインデントや改行を含む、人間が読みやすい形式(Pretty Print)にします。デバッグ時に非常に役立ちます。
serializeNulls() デフォルトでは、Gsonは値がnullのフィールドをシリアライズ時に無視(出力しない)します。このメソッドを呼び出すと、nullのフィールドも"fieldName": nullという形式でJSONに出力されるようになります。
setDateFormat(String pattern) java.util.Date型のフィールドを、指定したパターン(例: "yyyy-MM-dd HH:mm:ss")でシリアライズ・デシリアライズします。
addSerializationExclusionStrategy(ExclusionStrategy) シリアライズ時に特定のフィールドを除外するためのカスタム戦略(ExclusionStrategy)を追加します。
registerTypeAdapter(Type, Object) 特定の型に対して、カスタムのシリアライザやデシリアライザを登録します。
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class GsonBuilderExample {
    public static void main(String[] args) {
        // GsonBuilderを使ってカスタマイズ
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()  // Pretty Printを有効化
                .serializeNulls()     // null値もシリアライズ
                .setDateFormat("yyyy/MM/dd") // Date型のフォーマットを指定
                .create();

        User user = new User("Jiro Suzuki", 35, null, true, null, null); // emailなどがnull

        String jsonOutput = gson.toJson(user);
        System.out.println(jsonOutput);
    }
}

実行結果 (Pretty Printed JSON):

{
  "name": "Jiro Suzuki",
  "age": 35,
  "email": null,
  "isDeveloper": true,
  "address": null,
  "skills": null
}

setPrettyPrinting()によって整形され、serializeNulls()によってnullのフィールドも出力されていることが確認できます。

@SerializedName: JSONキー名とフィールド名のマッピング

JSONのキー名がJavaの命名規則(例: snake_case)と異なり、Javaクラスのフィールド名(例: camelCase)と直接マッピングできない場合があります。このような場合、@SerializedNameアノテーションが役立ちます。

import com.google.gson.annotations.SerializedName;

public class Product {
    // JSONの "product_id" キーをこのフィールドにマッピング
    @SerializedName("product_id")
    private int productId;

    // "productName" はキー名が同じなのでアノテーション不要
    private String productName;

    // JSONの "price_in_yen" キーをこのフィールドにマッピング
    @SerializedName("price_in_yen")
    private double priceInYen;
    
    // 複数のキー名をマッピングすることも可能(デシリアライズ時)
    @SerializedName(value = "in_stock", alternate = {"is_available"})
    private boolean inStock;

    // コンストラクタ、getter/setterは省略
}

このアノテーションにより、Gsonはシリアライズ・デシリアライズ時に指定された名前を使ってマッピングを行います。alternate属性を使えば、デシリアライズ時に複数の異なるキー名に対応させることも可能です。

フィールドの除外: @Expose と ExclusionStrategy

特定のフィールドをJSON変換の対象から外したい場合があります。Gsonにはいくつかの方法が用意されています。

  1. transient キーワード: Javaのtransient修飾子が付いたフィールドは、デフォルトでシリアライズの対象外となります。最も手軽な方法ですが、Gson以外のシリアライズ機構にも影響を与える可能性があります。
  2. @Expose アノテーション: より細かく制御したい場合に用います。このアノテーションを使うには、GsonBuilderexcludeFieldsWithoutExposeAnnotation()を呼び出す必要があります。これにより、@Exposeが付与されたフィールドのみが変換対象となります。
    public class Employee {
        @Expose
        private String name;
    
        @Expose(serialize = true, deserialize = false) // シリアライズのみ対象
        private int employeeId;
    
        private String password; // @Exposeがないので対象外
    
        // ...
    }
    
    // Gsonインスタンスの生成
    Gson gson = new GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .create();
    
  3. ExclusionStrategy: 最も柔軟な方法です。特定のクラス型、アノテーション、フィールド修飾子など、独自の条件に基づいてフィールドを除外するロジックを実装できます。
    import com.google.gson.*;
    
    // 特定のアノテーションが付いたフィールドを除外する戦略
    public @interface SkipSerialization {}
    
    ExclusionStrategy strategy = new ExclusionStrategy() {
        @Override
        public boolean shouldSkipField(FieldAttributes f) {
            // @SkipSerializationアノテーションが付いていれば除外
            return f.getAnnotation(SkipSerialization.class) != null;
        }
    
        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            return false;
        }
    };
    
    Gson gson = new GsonBuilder()
            .setExclusionStrategies(strategy)
            .create();
    

カスタム(デ)シリアライザ: 日時など特殊な型の扱い

Gsonはjava.util.Dateには対応していますが、Java 8で導入された新しい日時API(java.time.LocalDate, java.time.LocalDateTimeなど)にはデフォルトで対応していません。これらの型を扱うには、カスタムのシリアライザとデシリアライザを登録する必要があります。

ここではLocalDateを例に、ISO 8601形式(例: “2025-07-14″)の文字列に変換するアダプタを作成します。

import com.google.gson.*;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

// LocalDate用のTypeAdapterを実装
class LocalDateAdapter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;

    @Override
    public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(FORMATTER.format(src));
    }

    @Override
    public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        return FORMATTER.parse(json.getAsString(), LocalDate::from);
    }
}

public class CustomSerializerExample {
    public static void main(String[] args) {
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(LocalDate.class, new LocalDateAdapter())
                .setPrettyPrinting()
                .create();

        Event event = new Event("Tech Conference", LocalDate.of(2025, 10, 24));
        String json = gson.toJson(event);
        System.out.println("Serialized JSON:\n" + json);

        Event deserializedEvent = gson.fromJson(json, Event.class);
        System.out.println("\nDeserialized Event Date: " + deserializedEvent.getDate());
    }
}

class Event {
    private String name;
    private LocalDate date;
    // コンストラクタ、getter/setter
}

このようにJsonSerializerJsonDeserializerインターフェースを実装したクラスを作成し、GsonBuilderregisterTypeAdapterで登録することで、あらゆるカスタム型に対応できるようになります。


第5章: 実践的なユースケースと注意点

Web APIとの連携

Gsonの最も一般的な用途は、REST APIとの通信です。クライアントとしてAPIにリクエストを送信する際は、リクエストボディとなるJavaオブジェクトをtoJson()でJSON文字列に変換します。逆にAPIからレスポンスを受け取った際は、そのJSON文字列をfromJson()でJavaオブジェクトに変換し、アプリケーションで利用します。

設定ファイルの読み書き

アプリケーションの設定をJSONファイルで管理する際にもGsonは便利です。設定項目を保持するクラスを定義し、起動時にJSONファイルを読み込んでfromJson()で設定オブジェクトを生成したり、設定変更時にtoJson()でファイルに書き出したりすることができます。

パフォーマンスに関する注意

前述の通り、Gsonインスタンスはスレッドセーフであり、生成にはコストがかかります。特にGsonBuilderで複雑な設定を行っている場合、そのコストは無視できません。アプリケーション内でインスタンスをシングルトンとして管理するか、DI(Dependency Injection)フレームワークを利用して注入するなど、インスタンスの再利用を心がけてください。

よくある例外: JsonSyntaxException

デシリアライズ時によく遭遇するのがJsonSyntaxExceptionです。これは、渡された文字列が有効なJSON形式ではないことを示しています。原因としては、JSONの構文エラー(カンマの付け忘れ、括弧の不一致など)、予期しないデータ型(数値であるべき箇所に文字列が入っているなど)が考えられます。この例外が発生した場合は、入力されたJSON文字列の内容をまず確認することが重要です。

Androidでの利用について

GsonはAndroid開発でも広く使われてきましたが、公式リポジトリでは注意が喚起されています。Gsonはリフレクションを多用するため、R8 (ProGuard) などによるコードの圧縮・難読化・最適化と相性が悪く、実行時エラーを引き起こす可能性があります。Androidのネイティブ開発では、リフレクションではなくコード生成を利用するMoshiKotlinx Serializationなどのライブラリの使用が推奨されています。


まとめ

本記事では、JavaライブラリGsonの基本的な使い方から、GsonBuilderや各種アノテーション、カスタムアダプタを用いた応用的なテクニックまで、幅広く解説しました。Gsonは、そのシンプルさと強力なカスタマイズ性により、JavaにおけるJSON処理のデファクトスタンダードの一つとしての地位を確立しています。

基本的なシリアライズ・デシリアライズは数行のコードで実現でき、複雑な要件にもExclusionStrategyやカスタムTypeAdapterで柔軟に対応できます。この記事で紹介した知識を活用すれば、あなたのJavaアプリケーションにおけるデータ交換処理を、より効率的で堅牢なものにできるはずです。

Gsonをマスターすることで、Web API連携や設定管理など、モダンなアプリケーション開発に不可欠なJSON操作を自信を持って行えるようになります。ぜひ、実際のプロジェクトで活用してみてください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です