Gson — Googleが提供するJavaオブジェクトをJSONにシリアライズするためのライブラリで、Android開発で広く使用されています。パーサーを手動で記述することなく、複雑なオブジェクトグラフをコンパクトなJSON文字列に変換できます。Google Gson、2024によると、このライブラリはGitHubで23,000以上のスターを獲得しており、JavaおよびKotlinエコシステムでJSONを扱うための最も人気のあるソリューションの1つであり続けています。
主要ポイント
Gsonは、オブジェクトをJSON表現に変換するためにGoogleが開発したJavaライブラリです。リフレクションを使用してクラス構造を分析するため、事前設定なしで動作します。Gsonは任意のJavaオブジェクト、コレクション、配列、ジェネリクス、ネストされたクラスをサポートしています。基本的な使用にはアノテーションは必要ありませんが、微調整のためにアノテーションを提供しています。リフレクションの主な欠点は、初期化中のパフォーマンス低下とコンパイル時の最適化が不可能なことであり、これはAndroidアプリケーションのコールドスタート時に数百のモデルをデシリアライズする際に特に顕著です。それにもかかわらず、Gsonはその安定性と豊富なドキュメントにより、ほとんどのプロジェクトで信頼できる選択肢であり続けています。
Gsonは2008年にGoogleによってリリースされ、すぐにAndroidアプリケーションにおけるJSONのデファクトスタンダードになりました。Moshiとkotlinx.serializationが登場する以前は、GsonがKotlinプロジェクトにおける唯一の一般的な選択肢でした。統合の容易さ — build.gradleに1つの依存関係を追加するだけ — と必須アノテーションがないことが、Gsonをあらゆるレベルの開発者の間で人気にしました。
// build.gradleへのGsonの追加
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
}
// 基本的な使用法
data class User(
val id: Int,
val name: String,
val email: String
)
val gson = Gson()
val user = User(1, "John", "john@test.com")
val json = gson.toJson(user)
println(json) // {"id":1,"name":"John","email":"john@test.com"}
基本的なシリアライゼーションに加えて、Gsonは動作を設定するためのGsonBuilderを提供しています:日付フォーマット、HTMLエスケープの無効化、キーのケース、カスタムインスタンスなど。GsonBuilderは、ライブラリが自動的に処理できない型に対してカスタムJsonSerializerおよびJsonDeserializerを登録することもできます。設定の柔軟性により、GsonBuilderは最新のAndroid開発において特定のプロジェクト要件にライブラリを適応させる際に不可欠で便利なツールとなっています。
toJsonは、リフレクションを通じてフィールドを分析し、JavaオブジェクトをJSON文字列に変換します。デフォルトでは、Gsonはtransientおよびstaticを除くすべてのフィールドを含めます。このメソッドはプリミティブ、オブジェクト、コレクション、配列などあらゆる型をサポートします。fromJsonは逆の操作を実行し、JSON文字列とターゲットオブジェクトクラスを受け取り、フィールドが設定されたインスタンスを返します。
シリアライゼーション中、Gsonはネストされたフィールドを含むすべてのオブジェクトフィールドを再帰的にトラバースします。循環参照はStackOverflowErrorを引き起こすため、@Exposeアノテーションまたはカスタムアダプターを使用して除外する必要があります。コレクションの場合、Gsonは要素の型を保持しますが、ジェネリクスを含むリストをデシリアライズする際には、型情報を保持するためにTypeTokenが必要です。
// ネストされたオブジェクトを持つdata class
data class Address(
val city: String,
val street: String
)
data class Employee(
val id: Int,
val name: String,
val address: Address
)
val gson = Gson()
val employee = Employee(1, "Alice",
Address("New York", "5th Ave"))
// JSONへのシリアライズ
val json = gson.toJson(employee)
// JSONからのデシリアライズ
val jsonString = """
{"id":2,"name":"Bob","address":{"city":"London","street":"Baker St"}}
"""
val parsed = gson.fromJson(jsonString, Employee::class.java)
Gsonは、シリアライゼーションプロセスを管理するための一連のアノテーションを提供しています。@SerializedNameはフィールド名と異なるJSONキー名を指定します。@Exposeはフィールドがシリアライゼーションに含まれるかどうかを制御します:GsonBuilder.excludeFieldsWithoutExposeAnnotation()を介して作成されたGsonは、@Exposeが付いたフィールドのみを処理します。@Sinceと@Untilはフィールドのバージョニングを制御します。
@SerializedNameアノテーションは名前の不一致の問題を解決します:サーバーがsnake_caseを使用する一方で、コードではcamelCaseが使用される場合があります。このアノテーションは値と後方互換性のためのオプションの代替を受け入れます。@Exposeは、機密フィールド(パスワード、トークン)を@Expose(serialize = false)としてマークすることでシリアライゼーションから隠すことができます。包含と除外に加えて、@ExposeはGsonBuilder.excludeFieldsWithoutExposeAnnotationと組み合わせてフィールドのホワイトリストを作成することができ、多くのフィールドを持つオブジェクトをシリアライズする際の攻撃対象領域を制御するのに役立ちます。
// Gsonアノテーション付きモデル
data class UserResponse(
@SerializedName("user_id")
val userId: Int,
@SerializedName("full_name",
alternate = [Alternative("name")])
val fullName: String,
@Expose(serialize = false)
val password: String
)
// @Exposeフィルタリング付きGson
val gson = GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create()
val user = UserResponse(1, "John", "secret123")
println(gson.toJson(user))
// {"user_id":1,"full_name":"John"} — パスワードは除外
ジェネリクスの問題は、JavaとKotlinにおけるコンパイル時の型消去です。GsonがList<User>をデシリアライズするとき、要素の型がわからずList<Map<String, Any>>を返します。型情報を保持するために、GsonはTypeTokenを提供します — 匿名クラスを通じて型パラメーターをキャプチャする抽象クラスです。TypeTokenがない場合、開発者は各要素をMapからターゲット型に手動で変換する必要があり、コードが煩雑になりパフォーマンスが低下します。
TypeTokenは型消去の問題を解決します。開発者は必要な型パラメーターを持つTypeTokenの匿名サブクラスを作成し、Gsonはクラスシグネチャの情報を使用して正しくデシリアライズします。TypeTokenはMap、Set、およびネストされたジェネリクスを含む他のパラメーター化された型でも機能します。特にMap<String, List<User>>の場合、完全なネスト型シグネチャを持つTypeTokenが必要です。そうしないと、GsonはList<User>の代わりに値をList<Map<String, Any>>としてデシリアライズします。
// リストデシリアライズのためのTypeToken
data class Product(
val id: Int,
val title: String,
val price: Double
)
val jsonArray = """
[
{"id":1,"title":"Phone","price":599.0},
{"id":2,"title":"Laptop","price":1299.0}
]
"""
val gson = Gson()
val listType = object : TypeToken<List<Product>>() {}
val products: List<Product> =
gson.fromJson(jsonArray, listType.type)
// カスタムデシリアライザー
class LocalDateAdapter :
JsonDeserializer<LocalDate> {
override fun deserialize(
json: JsonElement,
typeOfT: java.lang.reflect.Type,
context: JsonDeserializationContext
): LocalDate {
return LocalDate.parse(json.asString)
}
}
カスタムシリアライゼーションロジックのために、GsonはJsonSerializerおよびJsonDeserializerインターフェースをサポートしています。これらはGsonBuilder.registerTypeAdapter()を介して登録され、ライブラリが自動的にシリアライズできない型(Java 8の日付、非標準の値を持つEnum、ソースコードにアクセスできないサードパーティクラスなど)を処理できます。アダプターを実装する際は、パフォーマンスを監視することが重要です:カスタムアダプター内でリフレクションを呼び出すと手動制御の利点が無効になるため、直接的なメソッド呼び出しとフィールドアクセスが推奨されます。Gsonエコシステムには、UUID、Optional、Joda-Timeの日付ホイールなどの一般的な型に対するアダプターを提供するgson-extrasモジュールもあります。
GsonBuilderはシリアライゼーションの微調整のための数十のメソッドを提供しています。setPrettyPrintingは出力JSONにインデントと改行を追加して読みやすくします。disableHtmlEscapingは文字列内のHTML文字エスケープを無効にします。setDateFormatは日付形式を指定します。これは非標準の時刻表現を使用するサーバーと連携する際に重要です。setLenientは緩やかなパースモードを有効にし、特定のJSONフォーマットエラーを無視します。addDeserializationExclusionStrategyはカスタム戦略に基づいてプログラムでフィールドをデシリアライズから除外できます。デバッグには、setPrettyPrintingとロギングを組み合わせると便利です — JSONレスポンスがログで読みやすくなり、不一致の発見が容易になります。
GsonBuilderの重要な機能は、@Sinceおよび@Untilアノテーションによるフィールドのバージョニング管理です。開発者はsetVersionでオブジェクトのバージョンを指定し、Gsonはバージョンアノテーションに基づいてフィールドを自動的に含めるか除外します。これは、同じモデルがサーバープロトコルの異なるバージョンで使用されるAPI進化の際に便利です。GsonBuilderは、ファミリー型のグローバル処理のためのTypeAdapterFactoryの登録や、複雑なMapキーを正しく処理するためのcomplexMapKeySerializationもサポートしています。
よくある質問
Gsonは、JavaオブジェクトをJSONに変換するためのGoogleのライブラリです。サーバーレスポンスの解析、リクエストのシリアライズ、ローカルストレージへのデータ保存のためにAndroidアプリケーションで広く使用されています。
デフォルトでは、Gsonはシリアライゼーション中にnullのフィールドをスキップします。null値を含めるには、GsonBuilder.serializeNulls()を使用します。デシリアライゼーション中、JSONにないフィールドはnullのままか、その型のデフォルト値を取ります。
MoshiはKotlinクラスにリフレクションを使用しないため、より高いパフォーマンスと予測可能な動作を提供します。MoshiはKotlinのnull安全性も正しく処理しますが、Gsonはnullを非nullフィールドにデシリアライズして例外を引き起こす可能性があります。
@SerializedNameは、名前が一致しない場合にJSONキーをクラスフィールドにバインドします。たとえば、kotlinNameフィールドと"kotlin_name" JSONキーの場合、@SerializedName("kotlin_name")アノテーションが正しい変換を保証します。
TypeTokenは、匿名クラスを通じて型パラメーターをキャプチャする抽象クラスです。型消去のためGsonは実行時に要素の型を復元できないため、コレクションや他のパラメーター化された型をデシリアライズする際に必要です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。