null フィールド用 Option<T>

null になりうるフィールドを Option<T> でラップします。serde が「キーが欠けている」と「明示的な null」をどう区別するかも解説します。

Type Mapping

詳細な説明

Option で null 可能フィールドをモデル化する

Rust にはネイティブな null がありません。「値があるかもしれないし、ないかもしれない」を表す慣用的な方法は Option<T> です。Some(value) は存在、None は欠落を意味します。

JSONの例

{
  "id": 1,
  "nickname": "ada",
  "deleted_at": null
}

生成されるRust

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Root {
    pub id: i32,
    pub nickname: String,
    pub deleted_at: Option<serde_json::Value>,
}

内側の型を絞り込む

null の背後にある型は推論不能なので、判明したら Option<serde_json::Value> を実際の型に置き換えてください。

pub deleted_at: Option<chrono::DateTime<Utc>>,

キー欠落 vs 明示的 null

デフォルトでは serde はキーの欠落をデシリアライズエラーとして扱います。「キーが無い」場合も「null が来た」場合も両方受け入れたいときは、default 属性を併用してください。

#[serde(default)]
pub deleted_at: Option<DateTime<Utc>>,

これでキーが無い場合も null が来た場合も、フィールドは None になります。

シリアライズ時に null を省く

None のときに "deleted_at": null を出力したくない場合は、Option<T> に加えて #[serde(skip_serializing_if = "Option::is_none")] を付けます。

#[serde(skip_serializing_if = "Option::is_none")]
pub deleted_at: Option<DateTime<Utc>>,

これは PATCH リクエスト本文、スパースな REST レスポンス、列を NULL で上書きしたくないデータベース更新ペイロードでの定石パターンです。

ユースケース

ソフト削除された行、任意メタデータ、部分的な PATCH 更新ペイロードなどはすべて Option<T> に依存しています。このパターンを正しく使えるかどうかが、安全な往復処理と意図しないデータ損失の境目になります。

試してみる — JSON to Rust Struct Converter

フルツールを開く