階層的JSONからのネストRust構造体
1階層ネストされたJSONオブジェクトを2つのRust構造体にマップします。子構造体名が親フィールドからどう導出されるかを学びます。
Basics
詳細な説明
ネストJSONを複数の構造体に分割する
Rust は TypeScript のような匿名インライン構造体をサポートしていません。各ネストJSONオブジェクトは独自の名前付き struct となり、親構造体は型付きフィールドとしてそれを保持します。
JSONの例
{
"user": {
"name": "Bob",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "62704"
}
}
}
生成されるRust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
pub user: User,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub name: String,
pub age: i32,
pub address: Address,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Address {
pub street: String,
pub city: String,
pub zip: String,
}
名前の付け方
構造体名は親フィールド名を PascalCase に変換したものになります(address → Address、shipping_info → ShippingInfo)。異なる2つのオブジェクトが同じ名前に正規化される場合は、コンパイルエラーを避けるために数値サフィックス(Address、Address2)が付加されます。
なぜ重要か
ネストオブジェクトを名前付き型として切り出すと、再利用が可能になります。fn validate(addr: &Address) -> bool のような関数を書けば、由来を問わずあらゆる Address 値に対して呼び出せます。匿名のインライン型では、呼び出しごとにフィールドリストを再記述する必要が出てしまいます。
モジュール構成のヒント
非常に大規模な API を扱う場合は、各構造体を models モジュール下の個別ファイルに移し、mod.rs から再エクスポートするのが読みやすくなります。コンパイラはどちらでも構いませんが、人間にとっては大きな差です。
ユースケース
Stripe、GitHub、Slack といった外部 API のほとんどは、2〜3 階層のネストオブジェクトを持つレスポンスを返します。階層全体を一発で生成できるため、入力ミスを排除しつつ作業時間を大幅に短縮できます。