這個工具的用途
使用步驟
貼上 JSON(或載入範例),即可在右側讀到 Rust struct。每個巢狀物件 成為獨立 struct 並帶 #[derive(Serialize, Deserialize)];在 Cargo.toml 加上 serde = { version = "1", features = ["derive"] } 就可運作。
輸入: {"id":42,"name":"devsmiths","createdAt":"2024-03-11T08:24:00Z","stars":1280,"public":true,"contributors":[{"login":"ada","commits":51,"admin":true},{"login":"linus","commits":33,"admin":false}],"homepage":null}
輸出(Rust):
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Root {
pub id: i64,
pub name: String,
#[serde(rename = "createdAt")]
pub created_at: String,
pub stars: i64,
pub public: bool,
pub contributors: Vec<Contributor>,
pub homepage: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Contributor {
pub login: String,
pub commits: i64,
pub admin: bool,
}限制與邊界情況
- 預設 derives 是
Debug、Serialize、Deserialize。需要的型別請手動加上Clone、PartialEq或Hash——產生器保守是因為 derive everything 會拖累編譯時間。 - 不是合法 snake_case Rust 識別字的 JSON 鍵會加上
#[serde(rename = "…")]。 若整個 struct 的鍵都採同一 casing,改用 struct 層級的#[serde(rename_all = "camelCase")]較精簡—— 手動整併即可,產生器逐欄輸出 rename。 - 整數預設為
i64。若 ID 一定為正,可手動改u64; serde 兩者皆可,但 Rust 的型別系統能讓 invariant 免費。 - 可為 null 的欄位變為
Option<T>。若也希望缺漏鍵 反序列化為None,請加上#[serde(default)](切換 all optional 時產生器會自動加上)。 - 樣本中為
null的欄位變為Option<serde_json::Value>——產生器沒有資訊判斷 真實型別。確認後請手動收緊。多型情況可用#[serde(untagged)]在 enum 上。 - 日期字串保持為
String。若需要型別化日期,引入chrono或timecrate,改寫欄位型別並加上#[serde(with = "chrono::serde::ts_seconds")]。
常見問題
- 為什麼只有 Debug + Serialize + Deserialize — Clone 呢?
- 保守預設。為每個型別 derive Clone、PartialEq、Hash、Eq 會拖累編譯時間(每個 derive 都會跑 proc-macro),且多數 JSON DTO 不需要全部。請為特定 struct 手動加 `#[derive(Clone, PartialEq)]`。若多數型別都需要 Clone,一行 sed 即可:`s/Debug, Serialize, Deserialize/Clone, Debug, PartialEq, Serialize, Deserialize/`。
- 為什麼產生器逐欄輸出 serde rename,而不是 #[serde(rename_all = "camelCase")]?
- 因為真實 payload 中的 struct 不一定每個欄位都採同一 casing(legacy JSON 常混用 camelCase 與 snake_case)。逐欄 rename 永遠安全;`rename_all` 只在每個鍵都符合該慣例時安全。確認 struct 的鍵 casing 一致後可手動整併——struct 上的 `rename_all` 較短且編譯較快。
- 如何讓缺漏鍵反序列化為 None 而非錯誤?
- 兩種方式。欄位層級:在欄位旁加 `#[serde(default)]`——serde 使用 `Option::default()`(即 `None`)填入缺漏鍵。Struct 層級:`#[serde(default)]` 放在 struct 上會對每個欄位呼叫 `Default::default()`,需要實作或 derive `Default`。產生器的「all optional」會在每個 Option 欄位上輸出欄位層級的形式。
- 為什麼 null 欄位被標為 Option<serde_json::Value>?
- 因為樣本只顯示該欄位為 null,產生器沒有依據去產生真實型別。`serde_json::Value` 是逃生口——能反序列化任何東西。請手動收緊:若你知道該欄位有時是字串,改為 `Option<String>`;若是多型 union,用帶 `#[serde(untagged)]` 的 enum。
- ISO 8601 日期字串可以變成 chrono::DateTime 嗎?
- 不會自動。把 `created_at: String` 改為 `created_at: DateTime<Utc>`,加上 `#[serde(with = "chrono::serde::ts_seconds")]` 或 `chrono::serde::ts_rfc3339`(依格式)。若用 `time` crate(更接近 stdlib 的替代),則為 `OffsetDateTime` 配 `serde-well-known` 功能。
- i64 / u64 / f64 與 wire format 的差距有多大?
- JSON 只有一種數字型別——JavaScript 的雙精度浮點。2^53 以下的值是精確整數;再大就會在資料到達 serde 之前已失去精度。對 64-bit 無號 ID(Discord snowflake、Twitter ID 等),producer 應將其序列化為字串,然後用 `#[serde(deserialize_with = ...)]` 配字串到 u64 的輔助函式。產生器無法從樣本偵測這點。
內容審閱者:ShiangYu Huang