階層的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 に変換したものになります(addressAddressshipping_infoShippingInfo)。異なる2つのオブジェクトが同じ名前に正規化される場合は、コンパイルエラーを避けるために数値サフィックス(AddressAddress2)が付加されます。

なぜ重要か

ネストオブジェクトを名前付き型として切り出すと、再利用が可能になります。fn validate(addr: &Address) -> bool のような関数を書けば、由来を問わずあらゆる Address 値に対して呼び出せます。匿名のインライン型では、呼び出しごとにフィールドリストを再記述する必要が出てしまいます。

モジュール構成のヒント

非常に大規模な API を扱う場合は、各構造体を models モジュール下の個別ファイルに移し、mod.rs から再エクスポートするのが読みやすくなります。コンパイラはどちらでも構いませんが、人間にとっては大きな差です。

ユースケース

Stripe、GitHub、Slack といった外部 API のほとんどは、2〜3 階層のネストオブジェクトを持つレスポンスを返します。階層全体を一発で生成できるため、入力ミスを排除しつつ作業時間を大幅に短縮できます。

試してみる — JSON to Rust Struct Converter

フルツールを開く