シリアライゼーションとデシリアライゼーションは、オブジェクトを送信または保存用の形式に変換する基本的なプロセスです。モバイル開発では、これらのメカニズムはすべてのネットワークリクエスト、状態保存、プロセス間通信で使用されます。MDN Web Docs, 2024によると、JSONはWebとモバイルアプリケーションで最も人気のあるシリアライゼーションフォーマットであり、XMLやProtocol Buffersを凌駕しています。
主要なポイント
シリアライゼーションとは、アプリケーションオブジェクトをネットワーク送信またはディスク保存に適した形式に変換するプロセスです。デシリアライゼーションは受信したデータからオブジェクトを復元する逆変換を行います。モバイル開発では、シリアライゼーションはAPIリクエスト、画面状態の保存、キャッシュ、IntentやBundleを介したデータ転送で使用されます。
アプリケーションコンポーネント間またはアプリケーションとサーバー間のやり取りには、シリアライゼーションが必要です。REST APIはJSONまたはXMLでデータを送信し、gRPCはProtocol Buffersを使用し、Androidのプロセス内通信はParcelableを使用します。シリアライゼーションがなければ、複雑なオブジェクトをプロセス境界を越えて渡したり、データベースに保存したりすることは不可能です。
// シリアライゼーションの一般原則
data class User(
val id: Int,
val name: String,
val email: String
)
// シリアライゼーション: オブジェクト -> JSON
fun serializeUser(user: User): String {
return """{"id":${user.id},"name":"${user.name}","email":"${user.email}"}"""
}
// デシリアライゼーション: JSON -> オブジェクト
fun deserializeUser(json: String): User? {
// JSONをオブジェクトに解析
return Gson().fromJson(json, User::class.java)
}
| フォーマット | サイズ | 速度 | 可読性 | 型付け |
|---|---|---|---|---|
| JSON | 中 | 高 | 高 | 動的 |
| XML | 大 | 中 | 高 | XSDスキーマ |
| Protocol Buffers | 小 | 非常に高 | 低 | 厳格な.proto |
| FlatBuffers | 小 | 最大 | 低 | 厳格な.fbs |
JSON(JavaScript Object Notation)は、JavaScriptオブジェクト構文に基づく軽量なテキスト形式です。JSONは文字列、数値、ブール値、配列、ネストされたオブジェクトをサポートしており、モバイルアプリケーションのほとんどのデータ転送シナリオをカバーします。この形式はプラットフォームに依存しません。各エコシステムには、解析のための組み込みツールが用意されています。
JSONはそのシンプルさと汎用性で優れています。基本的な使用にはスキーマは不要で、構造は解析中に動的に決定されます。フォーマットの可読性はデバッグとテストを簡素化し、サーバーの応答は任意の開発者ツールで確認できます。両方のモバイルプラットフォームで、JSONはサードパーティライブラリを必要とせずネイティブに処理されます。
// Codable — SwiftでのネイティブJSONシリアライゼーション
struct User: Codable {
let id: Int
let name: String
let email: String
}
let jsonString = """{"id":1,"name":"John","email":"john@test.com"}"""
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder()
let user = try! decoder.decode(User.self, from: jsonData)
Protocol Buffers(protobuf)は、高性能システム向けにGoogleが開発したバイナリシリアライゼーションフォーマットです。JSONとは異なり、protobufは.protoファイルで事前定義されたスキーマを必要としますが、データサイズが大幅に小さく、処理速度が向上します。このフォーマットはgRPC、Firebase Firestore、Googleの内部サービスで使用されています。
protobufスキーマは、一意の番号を持つ型付きフィールドを持つメッセージを記述します。protocコンパイラは、対象言語でシリアライゼーションとデシリアライゼーションを自動的に実行するクラスを生成します。Protobufは、後方互換性を失うことなくフィールドを追加および削除するルールを通じてスキーマの進化をサポートします。
// user.protoでのスキーマ定義
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
repeated string roles = 4;
}
// Kotlinで生成されたコード
val user = UserProto.User.newBuilder()
.setId(1)
.setName("John")
.setEmail("john@test.com")
.build()
val bytes: ByteArray = user.toByteArray()
XML(eXtensible Markup Language)は、厳格な構造、名前空間のサポート、XSDスキーマによる検証を備えたフォーマットです。XMLはコンパクトさと解析速度でJSONに劣りますが、Android開発ではレイアウトファイル、AndroidManifest、リソース、Gradle構成で需要があります。iOSでは、XMLはplistファイルや一部のレガシーサービスで使用されています。
AndroidはXML解析に3つのアプローチを提供しています:DOM(ドキュメント全体をメモリにロード)、SAX(イベント駆動型ストリーム解析)、XmlPullParser(ハイブリッドアプローチ)です。XmlPullParserはモバイルデバイスに推奨されるオプションで、限られたメモリで効率的に動作し、読み取りながらドキュメントを処理できます。
// AndroidでのXmlPullParser
fun parseUserXml(inputStream: InputStream): User? {
val parser = Xml.newPullParser()
parser.setInput(inputStream, Xml.Encoding.UTF_8.name)
var id: Int? = null
var name: String? = null
var email: String? = null
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType == XmlPullParser.START_TAG
&& parser.name == "email") {
email = parser.nextText()
}
}
return User(id!!, name!!, email!!)
}
各プラットフォームは独自のシリアライゼーションツールを提供しています。iOSでは、主要なメカニズムはJSONEncoderとJSONDecoderを使用するCodableプロトコルで、Objective-CはNSJSONSerializationを使用します。AndroidはGson、Moshi、kotlinx.serializationライブラリを使用します。プロセス間データ転送には、AndroidはParcelableを、iOSはNSKeyedArchiverを使用します。Kotlinプロジェクトは、マルチプラットフォームをサポートしリフレクションに依存せず、Kotlinコンパイラプラグインを介してコンパイル時にシリアライザを生成するJetBrainsのソリューションであるkotlinx.serializationをますます選択しています。
AndroidはIntentとBundleに2つのシリアライゼーションメカニズムをサポートしています。Serializableはリフレクションを使用する標準的なJavaメカニズムで、パフォーマンスが低下します。ParcelableはAndroid固有のプロトコルで、writeToParcelメソッドとcreateFromParcelメソッドの手動実装が必要ですが、直接バイト操作により数十倍高速に動作します。
// AndroidでのParcelable
@Parcelize
data class UserParcel(
val id: Int,
val name: String,
val email: String
) : Parcelable
// Intentを介した転送
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user", userParcel)
startActivity(intent)
iOSでは、コントローラ間のデータ転送にはDataへの事前シリアライゼーションを行うCodableが使用され、プロセス間通信にはNSKeyedArchiverが使用され、オブジェクトをバイナリProperty List形式に変換します。最新のSwiftUIプロジェクトは、アプリケーションモジュール間の転送のためにJSONEncoderを介してデータをエンコードすることを好みます。
信頼できないデータの不適切なデシリアライゼーションは脆弱性につながる可能性があります。シリアライゼーション攻撃は、JavaのオーバーライドされたreadObjectメソッドやサードパーティライブラリの安全でないデシリアライザを悪用します。Androidでは、信頼できないソースからのデータにはSerializableを避け、代わりにParcelableまたはデシリアライゼーション後のすべてのフィールドの手動検証を使用する必要があります。iOSでは、JSONDecoderはデフォルトで型に厳格ですが、JSONSerializationを扱う場合、開発者は条件付きキャストを通じて型を確認する必要があります。
シリアライゼーションフォーマットを選択する際には、パフォーマンス要件、データサイズ、互換性が考慮されます。REST APIやマイクロサービスアーキテクチャには、JSONが最適な選択肢であり、すべてのプラットフォームと言語でサポートされています。高負荷システムや帯域幅が限られたモバイルアプリケーションには、サイズが小さくデシリアライゼーションが高速なProtocol Buffersが適しています。XMLは設定シナリオやレガシーシステムとの統合においてのみ正当化されます。最近のプロジェクトでは、デシリアライゼーションのステップが不要なバイナリ形式であるFlatBuffersも人気を集めており、ゲームエンジンやレイテンシが重要なアプリケーションで使用されています。各フォーマットには独自のニッチがあり、適切な選択はアプリケーションの速度と消費トラフィック量に直接影響します。
よくある質問
シリアライゼーションとは、アプリケーションオブジェクトをネットワーク経由で送信したりファイルに保存したりできる形式にパッケージ化することです。物体の写真を撮ると想像してください。写真がシリアライゼーションで、写真から物体を復元するのがデシリアライゼーションです。
JSONは人間が読めるテキスト形式で、スキーマは不要です。Protocol Buffersは必須スキーマ(.proto)を持つバイナリ形式で、サイズが大幅に小さく、速度がより高速です。Protobufは高負荷システムに選択され、JSONは汎用互換性のために選択されます。
XMLは構成ファイル(AndroidManifest、レイアウトリソース)、複雑なネストを持つドキュメント、XSDによる厳格な検証を必要とするシステムの標準として残っています。XMLは、名前空間のサポートが必要なSOAPプロトコルやレガシーシステムでも使用されています。
Codableは、自動シリアライゼーションとデシリアライゼーションのためにEncodableとDecodableを組み合わせたSwiftのプロトコルです。コンパイラは、構造体またはクラスのすべてのプロパティに対してencode(to:)メソッドとinit(from:)メソッドの実装を生成します。
Parcelableはリフレクションを使用せず、バイトストリームに直接フィールドを書き込むため、Serializableよりも大幅に高速に動作します。最新のデバイスでは、Parcelableの方が最大10倍高速になる可能性があります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。