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> に依存しています。このパターンを正しく使えるかどうかが、安全な往復処理と意図しないデータ損失の境目になります。