Moshi: 主要概念、Kotlin用JSONライブラリとその仕組み

著者: IT Sectr 公開日: 2026-03-15 読了時間: 8 分

MoshiはSquareが作成した現代的なJSONライブラリで、Gsonの制約を考慮してKotlinとAndroidのために特に設計されました。Kotlinのnull安全性と完全互換性があり、コンパイル時にコードを生成し、リフレクションを使用しないため、パフォーマンスと信頼性が向上します。Square Moshi, 2024によると、Moshiは予測可能なシリアライゼーションを提供し、あらゆるデータタイプのカスタムアダプターをサポートします。

ポイント

  • Moshi — リフレクションなしでKotlinとAndroidのためのSquareのJSONライブラリ
  • Kotlinアダプター — data class、デフォルト値、null安全性の組み込みサポート
  • @Json — フィールド名の設定とプロパティの無視を行うアノテーション
  • アダプター — @ToJsonと@FromJsonによるカスタムシリアライゼーションロジック
  • コード生成 — MoshiはkaptまたはKSPを通じてコンパイル時にアダプターを生成

Moshiとは

MoshiはJVM、Android、Kotlin MultiplatformのためのJSONライブラリで、Square(OkHttpとRetrofitの作成者)が作りました。Gsonとは異なり、Moshiはリフレクションに依存しません—アダプターは@JsonClass(generateAdapter = true)アノテーションを通じてコンパイル時に生成されます。これにより、Kotlin固有の構造体を扱う際にMoshiはより高速で安全、かつ予測可能になります。

理念とメリット

Moshiが先行技術と異なる主なポイントは、リフレクションを排斥したことです。リフレクションによりGsonは準備なしでどのようなクラスでも動作できますが、代価として起動が遅く、コンパイラによる最適化ができず、ランタイムエラーのリスクがあります。Moshiはコード生成のためにクラスの明示的な宣言が必要ですが、その代わりにハンドライティングコードの速度とコンパイル時の完全な型安全性を提供します。

kotlin
// build.gradleにMoshiを追加
dependencies {
    implementation "com.squareup.moshi:moshi:1.15.0"
    implementation "com.squareup.moshi:moshi-kotlin:1.15.0"
    kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.0"
}

// コード生成のシンプルモデル
@JsonClass(generateAdapter = true)
data class User(
    @Json(name = "user_id")
    val id: Int,
    val name: String,
    val email: String,
    val avatar: String? = null
)

// 使用方法
val moshi = Moshi.Builder()
    .build()
val jsonAdapter = moshi.adapter(User::class.java)

インストールと設定

Moshiを使い始めるには、build.gradleに依存関係を追加し、モデルをアノテートする必要があります。Moshi.Builderがエントリポイントとなり、標準型の組み込みアダプター、カスタムアダプターの追加やライブラリの動作設定を行います。MoshiはDate、Enum、Collection、Mapのアダプターをスタンダードでサポートしますが、Kotlinクラスにはmoshi-kotlinモジュールが必要です。Gsonとは異なり、MoshiはデフォルトでKotlinクラスにリフレクションを使用しません—このためにKotlinJsonAdapterFactoryを接続し、コード生成が使用されない場合やクラスが@JsonClassでアノテートされていない場合のバックアップとして機能します。このアプローチにより、開発者は各クラスについて、コード生成のパフォーマンスとリフレクションの柔軟性を明示的に選択できます。

Moshiの作成とアダプターの追加

Builderを通じてMoshiを構築した後、開発者はMoshiのインスタンスを取得し、必要なクラスのアダプターをリクエストします。JsonAdapterは中心となるオブジェクトで、toJson()によるシリアライゼーションとfromJson()によるデシリアライゼーションを実行します。クラスが@JsonClass(generateAdapter = true)でアノテートされている場合、Moshiは自動的に生成されたアダプターを使用し、そうでない場合はバックアップとしてリフレクティブなKotlinJsonAdapterFactoryを適用します。このアプローチは、様々な規模と複雑さのプロジェクトにおいて、コード生成の高速性とリフレクティブ機構の柔軟性を組み合わせます。Moshiは小規模なアプリケーションから数百のデータモデルを扱う大規模エンタープライズプロジェクトまで対応します。

kotlin
// KotlinJsonAdapterFactoryでMoshiを設定
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .add(LocalDateAdapter())
    .build()

// アダプターの使用
val adapter = moshi.adapter(User::class.java)

// シリアライゼーション
val user = User(1, "Alice", "alice@test.com")
val json = adapter.toJson(user)

// デシリアライゼーション
val jsonString = """{"user_id":2,"name":"Bob","email":"bob@test.com"}"""
val parsedUser = adapter.fromJson(jsonString)

// リストの扱い
val listAdapter = moshi.adapter(
    Types.newParameterizedType(
        List::class.java,
        User::class.java
    )
)

アノテーションとアダプター

Moshiはアノテーションを使用してシリアライゼーションを設定し、カスタムタイプをサポートします。@Json(name = "...")はフィールドのJSONキーを設定します。@Transientはフィールドをシリアライゼーションから除外します。@JsonClass(generateAdapter = true)はコード生成を有効にします。カスタムロジックには、Moshiは@ToJsonと@FromJsonアノテーションを提供し、別途のアダプタークラスに配置できます。

@Jsonとカスタムアダプター

@JsonアノテーションはGsonの@SerializedNameを置き換え、同様に動作します。kotlinNameフィールドが"kotlin_name"というJSONキーに結び付けられます。Moshiがデフォルトでシリアライズできないタイプ(例えばLocalDate)には、開発者が@ToJsonと@FromJsonメソッドを持つクラスを作成します。アダプターはMoshi.Builder.add()を通じて登録され、グローバルまたは特定のタイプに適用されます。Moshiはシールドクラスや、@JsonClassと明示的な区別子による多形シリアライゼーションをサポートし、手動のフィールド検査なしでJSONのタイプ階層を扱うことができます。デシリアライゼーション時、MoshiはデフォルトでJSONの不明なキーを無視し、クライアントコードを変更せずにサーバー側に新しいフィールドを追加する際の後方互換性を保証します。デバッグ用には、failOnUnknownで厳密モードを有効にすることができ、不明なキーが検出された場合に例外をスローします。

kotlin
// LocalDate用カスタムアダプター
class LocalDateAdapter {

    @ToJson
    fun toJson(date: LocalDate): String {
        return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
    }

    @FromJson
    fun fromJson(dateString: String): LocalDate {
        return LocalDate.parse(dateString)
    }
}

// Moshiアノテーションを持つモデル
@JsonClass(generateAdapter = true)
data class Event(
    @Json(name = "event_id")
    val id: Int,

    @Json(name = "event_date")
    val date: LocalDate,

    @Transient
    val localCache: String? = null
)

// アダプターの登録
val moshi = Moshi.Builder()
    .add(LocalDateAdapter())
    .add(KotlinJsonAdapterFactory())
    .build()

Moshi vs Gson

MoshiとGsonの比較は、AndroidプロジェクトのJSONライブラリ選択時によくある質問です。Moshiはコード生成、null安全性、高速性により、現代的なKotlin開発で優れています。GsonはJavaプロジェクト、レガシーコード、最小限の設定が重視されるシナリオでまだ役立ちます。差は大量のデータと複雑なモデルで顕著になります。

パフォーマンスと安全性

パフォーマンステストによると、コード生成を使用したMoshiは、シリアライゼーションおよびデシリアライゼーションの操作でGsonより2–5倍高速です。Moshiの主な利点はKotlinのnull安全性を正しく処理することです。JSONにフィールドが無く、モデルがデフォルト値なしでnon-nullを宣言している場合、Moshiはデシリアライゼーション時に例外をスローし、ヒッドエラーを防ぎます。

特徴GsonMoshi
メカニズムリフレクションコード生成 / リフレクション
Null安全性考慮しないKotlin完全サポート
速度中程度高速
デフォルト値サポートしないサポートする
Kotlin Multiplatformなしあり
ライブラリサイズ~240 Kb~150 Kb

MoshiとGsonの選択はプロジェクトのコンテキストによります。Kotlinの新規プロジェクトは、型安全性とパフォーマンスによりMoshiからメリットを得られます。GsonはJavaコードのサポート、動的なJSON構造、または設定の簡単さが速度よりも重視されるシナリオで依然として合理的な選択です。Kotlin Multiplatformにおいて、Moshiはこのプラットフォームをサポートする二つのオプションのうち唯一のライブラリです。

GsonからMoshiへの移行では、主にアノテーションとアダプターが変更になります。Gsonの@SerializedNameは@Json(name = "...")に置き換えられ、カスタムのJsonSerializer/JsonDeserializerは@ToJson/@FromJsonの組に置き換えられます。デフォルト値やnullableフィールドを持つモデルでは、Moshiはより予測可能に動作します。JSONにデフォルトのないnon-nullフィールドが無い場合、MoshiはJsonDataExceptionをスローし、ヒッドNPEを防ぎます。MoshiConverterFactoryを通じたRetrofitとの統合は、単一の依存関係で追加でき、ネットワーク層のアーキテクチャを変更する必要がありません。ProGuardやR8による隠し化には、@JsonClassでアノテートされたクラスと生成されたアダプターを保存するルールを追加する必要があり、そうしないとリリースビルドでシリアライゼーションが壊れます。総じて、GsonからMoshiへの移行は、パフォーマンスと型安全性が重要な新規Kotlinプロジェクトにおいて有理です。

kotlin
// シリアライゼーション比較: Gson vs Moshi
data class Sample(
    val name: String,
    val count: Int,
    val tags: List<String> = listOf()
)

// Gson: リフレクションで動作
val gson = Gson()
val fromGson = gson.fromJson("""{"name":"test"}""",
    Sample::class.java)
// count = 0 (デフォルト)、しかしnull安全性はチェックされない

// Moshi: アダプターが必要、null安全性は明示的
@JsonClass(generateAdapter = true)
data class SampleMoshi(
    val name: String,
    val count: Int,
    val tags: List<String> = listOf()
)

よくある質問

AndroidでのMoshiとは?

Moshiは、リフレクションの代わりにコード生成を使用する、SquareによるKotlinとAndroid向けのJSONライブラリです。高いパフォーマンス、Kotlinのnull安全性の正しい処理、Kotlin Multiplatformとの互換性を提供します。

MoshiはGsonよりどのように優れていますか?

Moshiは速度(コード生成により2–5倍高速)、安全性(Kotlinのnullアノテーションを尊重)、サイズ(約90 Kb小さく)でGsonを上回ります。MoshiはKotlin Multiplatformとdata classのデフォルト値もサポートします。

Moshiで@JsonClassアノテーションはどのように動作しますか?

@JsonClass(generateAdapter = true)は、指定されたクラスのアダプターをコンパイル時に生成するようMoshiに指示します。生成されたアダプターはリフレクションなしで直接シリアライゼーションを実行し、最大のパフォーマンスを発揮します。

カスタムMoshiアダプターを作るには?

@ToJson(シリアライゼーション)と@FromJson(デシリアライゼーション)でアノテートされたメソッドを持つクラスを作成します。Moshi.Builder.add()を通じてインスタンスを登録します。Moshiは対応するタイプの処理時に自動的にアダプターを見つけて適用します。

MoshiはKotlin Multiplatformをサポートしていますか?

はい、Moshiはバージョン1.13.0よりKotlin Multiplatformをサポートしています。これによりMoshiはKMPプロジェクトで唯一の人気JSONソリューションとなり、すべてのターゲットプラットフォームで共通のシリアライゼーションコードを使用できます。

まとめ

  • Moshi — リフレクションの代わりにコード生成を使用するSquareの現代的なJSONライブラリ
  • @JsonClass — アダプター生成用アノテーション、ハンドライティングコードの速度を提供
  • @Json — JSONキー設定、@Transient — フィールドをシリアライゼーションから除外
  • @ToJsonと@FromJson — あらゆるタイプのカスタムアダプターのためのシンプルなAPI
  • Null安全性 — MoshiはKotlinアノテーションを尊重し、不一致があれば例外をスロー
  • パフォーマンス — シリアライゼーションとデシリアライゼーションでGsonより2–5倍高速
  • Kotlin Multiplatform — 統合シリアライゼーションコードのKMPサポート

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください