把 JSON 轉換成 Zod schema,會產生可供檢視的 TypeScript 原始碼——一行 import、一個具名的 schema 表達式,以及一個推導出來的型別——這些都是嚴格根據單一 JSON 樣本的結構推導而來。輸出結果是在瀏覽器本機產生的,不會上傳,也不會在頁面上被執行。哪些欄位是必要、哪些是可選,是根據樣本實際包含的內容來決定:在每一個觀察到的物件中都出現的鍵,會變成必要欄位;在任何一個物件中缺席的鍵,會變成可選欄位;與另一種型別並存的 null,會變成可為 null;混合型別的陣列,則會變成一個結果確定的 z.union。這個 schema 只是一個起點,不是契約的證明:字串格式、數值範圍、整數限制、列舉、可辨識聯集、日期、URL、預設值、精緻化規則,以及業務規則,這些都刻意不做猜測,必須來自具權威性的文件。請在你自己的專案中安裝並釘選你所鎖定的 Zod 版本,編譯產生出來的原始碼,並在把結果當成真正的契約之前,先用可接受與應被拒絕的案例去測試它。

convert json to zod schema
把 JSON 轉換成 Zod schema

轉換器根據單一 JSON 樣本會產生什麼

JSON 轉 Zod Schema 轉換器接受單一一個合法的 JSON 值——可以是物件、陣列,或是基本型別——同時搭配一個 JavaScript 識別字,作為匯出的 schema 名稱與推導出的型別名稱。解析完成後,頁面會產生一段簡短、不依賴任何套件的原始碼片段:一行 import { z } from "zod"、一個形如 export const <Name> = z.object({ ... }) 的 schema 宣告(或是對應的陣列、基本型別,或聯集表達式),以及一個搭配的 export type <Name> = z.infer<typeof <Name>> 宣告。這個頁面不會載入 Zod、不會執行產生出來的原始碼,也不會把樣本傳到任何地方。產生的結果是純文字,你可以檢視、格式化,並複製到一個已經自行管控 Zod 版本的專案裡。

物件屬性會依照原始碼中出現的順序排列,因此產生出來的 z.object 區塊,順序會與你貼上的 JSON 一致。屬於合法 JavaScript 識別字的鍵,在輸出結果中不會加上引號。包含空白、連字號、開頭是數字、引號,或其他不適合當識別字的字元的鍵,則會以 JSON 字串逸出的方式輸出,讓外層的物件字面值仍然是合法的 TypeScript 原始碼。數字與其他數值不會被改寫,所以你貼上的內容,就是被拿來推導的內容。

把 JSON 轉換成 Zod Schema:三步驟流程

  1. 把一個合法且具代表性的 JSON 值貼進輸入區,並為這個 schema 輸入一個合法的 JavaScript 識別字。這個識別字會成為匯出的常數名稱,以及推導出的 TypeScript 型別名稱,所以請選一個與你程式碼中用來稱呼這個資料負載的名詞相符的名稱。
  2. 產生 Zod 原始碼,然後檢查輸出結果中,關於可選、可為 null、聯集、數值,以及空陣列的判斷是否合理。確認必要欄位與真正的契約一致,判斷可為 null 的標記是否符合你的 API,並檢查混合陣列與空陣列的行為是否符合你的預期。
  3. 把原始碼複製到一個已安裝你想要的 Zod 版本的專案中,格式化這段程式碼片段,用你的 TypeScript 建置流程編譯它,並在把它用於執行期驗證之前,用正式環境中應被接受與應被拒絕的案例來測試它。

必要、可選、可為 null,與聯集欄位是如何被推導出來的

從觀察到的 JSON,對應到官方文件記載的 Zod 建構函式,這個對應關係是固定且最小化的。字串對應到 z.string,有限的 JSON 數字對應到 z.number,布林值對應到 z.boolean,null 對應到 z.null。下表整理了轉換器對每一種可能遇到的結構情況,會如何處理。

樣本中觀察到的情況推導出的 Zod 表達式原因
每個範例中的物件都有相同的鍵z.object({ ... }),所有屬性皆為必要每一個被取樣的物件都有這個鍵,因此觀察到的結果是「必要」。
有物件在至少一個樣本中缺少某個鍵該屬性會被包在 .optional() 裡,其值型別會遞迴合併在部分樣本中缺席,代表僅憑這個樣本無法保證它一定存在。
屬性的值同時包含 null 與另一種型別合併後的型別會加上 .nullable()null 會被視為明確的「可為 null」標記,而不是另一個獨立的聯集成員。
同質陣列(每個元素的結構相同)單一元素 schema 被包在 z.array(...) 裡觀察到的結構只有一種且重複出現,因此同一個 schema 就能適用於每個位置。
混合基本型別或混合結構的陣列結果確定的 z.union([...])觀察到不同的元素結構,並以穩定的順序合併在一起。
空陣列z.array(z.unknown())沒有任何元素可供參考,因此轉換器拒絕用猜的。
巢狀陣列或巢狀物件遞迴套用同樣的規則推導會逐層深入,直到官方記載的巢狀層數上限為止。
物件陣列元素的鍵會跨所有被取樣的物件合併合併後會得到所有觀察到的鍵的聯集,並依照前述規則加上可選標記。

混合的值會以結果確定的順序排列,輸出結果中相同的 schema 也會被去除重複,因此轉換同一份 JSON 兩次,會產生穩定的原始碼,方便你在版本控制中乾淨地做比對。

轉換器刻意不會推導哪些內容

產生器拒絕去猜測任何單一樣本無法證明的限制條件。它不會推導字串格式、最小值、最大值、整數限制、列舉、字面值、可辨識聯集、日期、UUID、URL、電子郵件地址、品牌型別、型別強制轉換、預設值、轉換、精緻化規則、record、tuple、嚴格物件行為、遞迴參照,或業務規則。拼寫方式與小樣本無法確立真正的意圖,所以這些判斷留給你自己來做。請把產生出來的 schema 當成一個結構化的假設,只依照具權威性的文件來收緊它。

由此衍生出兩個實際的影響。第一,一旦你知道真正的契約內容,數值範圍就需要手動加上 .min()、.max() 或 .int()。第二,一個語意上其實是必要、但剛好在你這個單一樣本中缺席的欄位,會被標記為可選;在接受這個標記之前,請對照你的 API 規格、測試資料,或更廣泛的樣本集加以核對。官方的Zod API 參考文件列出了所有可以附加來精緻化輸出結果的方法,Zod 原始碼儲存庫則記載了每個版本的確切行為。

嚴格解析,以及你可能會碰到的硬性限制

在進行任何推導之前,這個頁面會先以嚴格模式解析 JSON,因此 JavaScript 註解、多餘的逗號、NaN、Infinity、undefined、BigInt 語法、單引號字串,以及物件字面值,都會被拒絕,而不是被悄悄修正。數字已經先經過 JSON.parse,因此會遵循 JavaScript 的數字精確度;需要精確十進位語意的識別碼或貨幣數值,通常應該保留為字串。只要碰到任何一項限制,就會產生明確的錯誤,而不是產生一個不完整的 schema,所以你不會在不知道轉換已經中止的情況下,貼上一段被推導到一半的片段。

限制項目上限值限制的對象
輸入大小500,000 個字元你貼上的 JSON 樣本
走訪過的值50,000 個值推導過程中走訪的節點總數
巢狀深度40 層最深的遞迴深入層數
產生出的原始碼1,000,000 個字元產生出的 Zod 文字長度

即使在這些限制範圍之內,過深或過寬的範例,仍然可能是品質不佳的契約文件,因此建議使用小型且具代表性的測試資料,並把彼此獨立的資料負載拆成不同的 schema。

根據你真正的契約驗證輸出結果

複製原始碼之後,請安裝你打算依賴的 Zod 版本,用你專案的格式化工具整理這段程式碼片段,編譯它,並用你真實測試資料中的正常案例與應被拒絕的案例來測試它。如果你習慣把 JSON 樣本轉換成其他有型別的宣告,同樣「先看原始碼」的原則也適用——舉例來說,用 Serde Derive 把 JSON 轉換成 Rust Struct這個流程,同樣是從單一樣本出發,並把結果當成一份可供檢視的草稿,而不是一份完成的契約。Zod 轉換器遵循同樣的理念:這個 schema 只告訴你單一樣本能證明什麼,其餘的驗證,則存在於你依照具權威性的文件、親手加上去的規則裡。

想深入了解,可參考如何驗證 JSON 語法並精確定位錯誤