不會。JSON 轉 Rust Struct 轉換器的輸出無法在不加入相依套件的情況下編譯,因為該頁面只會產生原始程式碼文字,絕不會幫你的專案補上建置所需的 crate。產生器會產生出帶有 #[derive(Serialize, Deserialize, Debug)] 標註的 Rust struct,並加上選擇性的 #[serde(rename = "...")] 屬性,但這些屬性需要 Serde 框架才能解析,而且解析原始的 JSON 通常還需要配套的 crate serde_json。兩個 crate 都必須已經在目標專案的 Cargo.toml 中宣告,且版本必須與產生程式碼所期待的 derive 巨集相容。該頁面本身絕不會上傳你的 JSON、絕不會呼叫 rustc 或 cargo,也絕不會在使用路徑、生命周期、泛型、自訂序列化器或模組可視性方面,幫你插入到產生的程式碼中。把輸出當成自給自足的 crate 來處理,會讓複製過去的程式碼區塊無法編譯。比較正確的心智模型是:轉換器交給你的是一份 struct 草稿,它仍必須放進一個相依套件都已經接好的專案裡。

does the output compile without dependencies when i convert json to rust struct
不加入相依套件時,JSON 轉 Rust 的輸出能編譯嗎

JSON 轉 Rust Struct 轉換器實際上會產生什麼

這個工具是一條嚴格 JSON 到原始碼文字的管線。你貼上一段 JSON 物件、陣列或基本型別,輸入一個 ASCII 根名稱,頁面就會輸出一個 Rust 模組內容。輸出是純文字,會依照你對 rustfmt 預期行為的縮排規則排版,裡面沒有 use 行、沒有 mod 行,也沒有 Cargo.toml 片段。

JSON 對應到 Rust 的規則是固定的、可供檢視:

JSON 值產生的 Rust 型別
StringString
Booleanbool
在 i64 範圍內的安全整數i64
其他 JSON 數字(浮點數、範圍外)f64
非空陣列Vec<T>,其中 T 是合併後的元素型別
空陣列Vec<serde_json::Value>
單獨的 nullOption<serde_json::Value>
與某個觀察型別並存的 nullOption<T>
物件帶有 Serialize、Deserialize、Debug derives 的公開 struct
不相容的混合值serde_json::Value

每個物件都會變成一個公開的 struct,而巢狀物件會變成具名的巢狀 struct,其型別名稱是依據它們在 JSON 樹中的路徑推導出來的。當陣列中含有多個物件時,在每個物件中都觀察到的欄位會保持為必填;至少在一個物件中缺席的欄位則會變成 Option<T>。整數與小數的觀察結果會向下合併為 f64,因為單一樣本無法判定究竟應該是 i64、u64、i128 還是 Decimal。遇到不相容的混合證據時,會退而使用 serde_json::Value,而不是默默地選用第一個型別。

為什麼你的專案需要 Serde 與 serde_json

產生的 #[derive(Serialize, Deserialize, Debug)] 程式行並非魔法,它們會展開為 Serde 框架所提供的 trait 實作,Serde 框架的說明文件位於 Serde 網站。如果你的相依套件樹中沒有 Serde,derive 巨集就無法解析,編譯器會回報 Serialize、Deserialize 與 Debug 為未定義。當你把輸出貼進一個全新的專案時,就會看到這個錯誤。

光有 Serde 還不足以解析你原本餵進去的 JSON。反序列化的執行階段是由 serde_json 提供的,它是另一個 crate,實作了 JSON token 的 Deserializer trait。產生的 struct 會透過 Deserialize 實作來呼叫那個 trait,因此你實際用來載入 JSON 的檔案,仍必須把 serde_json 帶進作用域。這兩個 crate 通常是用 cargo add serde --features derive 與 cargo add serde_json 加入,然後在 Cargo.toml 中釘住版本。頁面本身完全不會做這些事,它只是一個程式碼產生器。

這裡有一個值得強調的相關細節。空陣列的 Vec<serde_json::Value> 退路、單獨 null 的 Option<serde_json::Value> 退路,以及不相容混合值的 serde_json::Value 退路,全都假設目標程式碼能夠存取 serde_json::Value。如果移除這個相依套件或是為它取了別名,那麼任何退而使用該型別的欄位都會在不知不覺中損壞。請把產生的 serde_json::Value 型別參照視為一份真實的相依合約,而非裝飾性質的標記。

在不破壞編譯的前提下使用轉換器

正確的使用方式,是把 JSON 轉 Rust Struct 轉換器 當作一個已經備妥相依套件的專案內部的草稿撰寫步驟。乾淨的工作流程如下:

  1. 把一份具代表性的嚴格 JSON 樣本貼進輸入區,並輸入一個 ASCII 根型別名稱,例如 UserProfile 或 OrderEvent。根名稱必須是合法的 Rust 識別字,因為它會變成一個公開的 struct。
  2. 產生原始碼並檢視輸出。請特別注意數值寬度(樣本中的小整數在正式環境可能需要 u64、i128 或字串)、Option 的擺放位置(任何被觀察為 null 或在同層物件中缺席的欄位)、巢狀 struct 的名稱(確認它們在你的程式碼庫中讀起來合理),以及 serde 的重新命名(每個 rename 會在線上保留原本的 JSON 鍵值)。
  3. 把產生的原始碼複製到一個已啟用 Serde 的 Rust 專案。請確認 Cargo.toml 已經釘住相容版本的 serde(需含 derive 功能)以及 serde_json。該頁面只會輸出原始程式碼文字,絕不會修改 Cargo.toml。
  4. 用 cargo fmt 格式化複製過去的原始碼,讓它與 crate 的其他部分一致,然後執行 cargo build 確認能編譯。
  5. 用真實的測試資料做來回測試:用 serde_json::from_str::<RootType>(...) 讀取 JSON 檔案,再用 serde_json::to_string(&value) 將其序列化回去,然後比對兩段字串。乾淨的來回表示推斷出來的型別與線上格式相符。

檢視數值寬度、選項與重新命名

從單一 JSON 樣本所做的推論並不是合約。在你信任產生的原始碼之前,有三類檢視值得明確地關注。

數值觀察結果。落在 JavaScript 安全整數範圍內的 JSON 數字會對應到 i64。一旦超出該範圍,且頁面無法精確表示不安全整數的拼寫方式(因為 JSON 數字在推論前會先被 JavaScript 解析),就會退而使用 f64。如果你的正式系統會輸出 64 位元無號識別字、有號 128 位元計數器、十進位貨幣,或字串化後的 BigInt,這些在產生之後全都需要手動編輯。請在原始合約中把對精度敏感的識別字與小數保留為字串,頁面就會正確地遵循該合約。

選用與合併欄位。在陣列中至少一個物件裡缺席的鍵,會變成 Option<T>。本身就以 null 形式存在的欄位,會變成 Option<serde_json::Value>。與某個觀察到的非 null 型別並存的欄位,則會變成 Option<T>。這些擺放方式並不能保證正式環境的行為;它們只反映你所貼上的樣本。

識別字正規化。JSON 鍵會被轉換為 ASCII snake_case 的 Rust 欄位。駝峰式邊界、空格、標點符號與連字號會變成底線。前導數字會被加上前綴,Rust 關鍵字會加上尾端底線,而正規化時發生的衝突則會加上數字後綴(所以 user 與 User 都會被正規化為 user,接著 user 與 user_2)。當 Rust 欄位名稱與 JSON 鍵不同時,#[serde(rename = "original-key")] 屬性會保留線上名稱。Rust 參考手冊 中的命名規則定義了哪些算作合法識別字;頁面會遵循這些規則,使輸出能在任何合規的 Rust 工具鏈中編譯。

用真實測試資料對產生的原始碼做來回測試

複製過去的原始碼一旦能編譯,下一步就是把具代表性的酬載進行序列化與反序列化,並比對結果。來回測試能抓出產生器從單一樣本無法解決的推論情況。舉例來說,如果樣本使用 "id": 1,推斷出來的欄位就是 i64。如果真實測試資料接著出現 "id": 9999999999999999999,則反序列化會依消費端的不同而靜默失敗或顯示剖析錯誤。對成功與失敗的測試資料都做來回測試,是防止這類錯誤的務實手段。

關於用 Serde 將 struct 轉回 JSON 字串的配套指南 走的是反向路徑,在這裡很實用,因為它展示了同一組衍生出來的 trait 在兩個方向上的運作方式。一旦你確認 serde_json::from_str 能剖析你的測試資料,且 serde_json::to_string 能重現線上格式,就可以放心地把產生的型別送交版本管控。八份外部測試資料會把 String、i64、f64、bool、Vec、Option、null 與關鍵字重新命名語法的行為鎖住,額外的測試則涵蓋巢狀 struct、正規化衝突與無效輸入。

限制與這個工具不會補上的東西

產生器有明確的上限,會決定工作是否能完成。工作量上限為輸入 500,000 個字元、50,000 個值、40 層巢狀,以及 1,000,000 個輸出字元。一旦超過這些上限,頁面就會停止,該工作也就不再屬於這個工具的範圍。

產生器同樣不會補上一長串正式環境可用的 Rust 型別常會用到的東西。它不會加上 crate 路徑、模組可視性政策、生命周期、泛型、自訂序列化器、deny_unknown_fields、預設值、扁平化、標記或驗證。必須使用嚴格 JSON;註解、尾端逗號、NaN、Infinity、BigInt 語法、undefined 以及 JavaScript 物件實字都會被拒絕。如果你的輸入不是嚴格 JSON,請先用 JSON 驗證器或 JSON 格式化工具清理,再回到轉換器。

輸出就是純粹的 Rust 原始程式碼文字。請把它視為一份可供檢視的草稿。格式化它,在一個相依套件都已釘住版本的專案裡編譯它,並在把它當作你的 schema 採用之前,先用具代表性的測試資料做一次來回測試。

如果你正在權衡各種選項,在瀏覽器端使用 JSON 轉 XML 是否安全?瀏覽器端指南 對此有詳細說明。