一個 JSON Schema 轉 Zod 的轉換器,僅從單一 JSON 值運作,會產生可供審閱的 Zod 原始碼,無需上傳且沒有執行階段依賴,但它刻意止步於無法證明你真實的 API 合約,因為單一樣本無法確立格式、範圍、列舉或業務規則。「JSON Schema to Zod」這個詞被用於兩種不同的工作,而這個區別會影響你在輸出中可以信任什麼。有些讀者指的是 JSON Schema 規範——Draft 7、2019-09 或 2020-12——一個帶有 type、properties、required、format、minimum 與 pattern 等關鍵字的詞彙表。其他讀者實際上手上只有一個 JSON 值,想要可以貼到 TypeScript 檔案中的 Zod 原始碼。JSON to Zod Schema Converter 採取第二條路徑:它解析一個嚴格的 JSON 值,以遞迴方式遍歷結構,並產生 Zod 宣告以及推論出的 TypeScript 型別。了解你擁有的是哪一項工作,會徹底改變在該綱要安全地用於驗證正式流量之前,還剩下哪些檢查項目。

json schema to zod converter
json schema to zod converter

JSON Schema 規範 vs JSON 樣本:選擇正確的起點

如果你的輸入是 JSON Schema 文件——一個字面物件,例如 {"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}——將其餵給以樣本為基礎的轉換器,只會得到一個描述該綱要物件自身形狀的 Zod 綱要,而不是該綱要本應驗證的資料。輸出會把 type 與 properties 當作普通物件上的普通屬性名稱來處理,這沒有用處。要翻譯真正的 JSON Schema 文件(Draft 7+)需要不同類型的程式庫,它會遍歷關鍵字樹並產生對應的 Zod 呼叫,例如 z.object({ id: z.string() }),並對 minLength 等加上 .min(1) 的細部修飾。這些程式庫以 npm 套件形式存在;它們並不是你將一個值貼到以樣本為優先的轉換器時在瀏覽器中執行的東西。

以樣本為優先的方法真正擅長的是更常見的真實世界任務:你從隊友、API 回應或儲存的固定資料中收到一個 JSON 承載,並想要一個可以編輯的起始 Zod 綱要。轉換器把那一個觀察結果轉成可匯入的原始碼,你可以審閱、與文件進行差異比對,並加以延伸。這個區別——規範翻譯 vs 樣本推論——是你在點擊產生之前應做的第一個決定。

產生器自動處理的型別對應

對基本型別而言,轉換是確定性的。每個觀察到的 JSON 值都會落入下列已記載的 Zod 建構子之一,而對同一個 JSON 重複進行轉換會產生穩定的原始碼,因為相同的綱要會被去重,混合值會以確定性的順序排列。

觀察到的 JSON 值產生的 Zod 運算式備註
字串,例如 "hello"z.string()不推論格式;請依文件手動加上 .email()、.url() 或 .uuid()。
有限數字,例如 42 或 3.14z.number()已通過 JSON.parse;視為 JavaScript 數字精度。
布林值 true 或 falsez.boolean()不對真值字串或 0/1 數字進行強制轉型。
nullz.null()與另一個觀察到的型別結合時會變成 .nullable()。
物件 {...}z.object({...})屬性依來源出現順序排列,而非字母順序。
陣列 [...]z.array(...)空陣列會產生 z.array(z.unknown()),而非猜測元素型別。

屬於合法 JavaScript 識別字的金鑰在輸出中保持不加引號。含有空格、連字號、數字開頭或引號的金鑰會以 JSON 字串跳脫形式輸出,以確保外層的 z.object 呼叫仍然是合法的原始碼。這讓產生的文字保持可讀性,同時仍能處理含有非慣例屬性名稱的 API 回應。

三個步驟將 JSON 值轉為 Zod 原始碼

以樣本為優先的工作流程刻意設計得很短。每一個步驟都有審閱關卡,讓你在推論選擇進入程式碼庫之前先抓住它們。

  1. 貼上一個有效且具代表性的 JSON 值,並輸入一個有效的綱要識別字,例如 UserSchema。僅接受嚴格 JSON——JavaScript 註解、尾端逗號、單引號、NaN、Infinity、undefined 與 BigInt 語法會被拒絕,而不是靜默修復。請使用一個能代表你真正想驗證之形狀的單一承載,而不是一個會隱藏可選欄位的精簡範例。
  2. 產生 Zod 原始碼,然後檢查輸出中的 optional、nullable、union、numeric 與 empty-array 選擇。留意樣本中至少一個物件缺少的屬性上的 .optional(),留意任何時候持有 null 的金鑰上的 .nullable(),並確認混合型別陣列以穩定順序產生了 z.union([...]) 項目。
  3. 將原始碼複製到已安裝 Zod 的專案中,並以權威的資料合約進行測試。將通過的正式案例與被拒絕的邊界案例都通過該綱要執行,而不只是原始樣本,以確認必填欄位、格式與業務規則都表現正確。

工作量的上限為 500,000 個輸入字元、50,000 個走訪值、40 層巢狀深度,以及 1,000,000 個產生的字元。觸及任何一個上限都會回傳明確的錯誤,並且永遠不會產生部分綱要。深度或寬度極大的範例在這些限制下仍可能是差勁的合約文件,因此請優先使用一個小型具代表性的固定資料,並將獨立的承載拆分到不同的綱要中。

在提交前你應該審閱的推論決策

樣本推論編碼了一些刻意選擇的規則,這些規則與人類撰寫綱要的方式不同。事先了解它們能避免在程式碼審閱時出現意外。

樣本中的情況產生器產生的結果原因
空陣列 []z.array(z.unknown())沒有未來元素型別的證據;明確表達不確定訊號,而不是靜默猜測為 z.string() 或 z.never()。
物件陣列具有重疊的金鑰合併金鑰,每個值以遞迴方式合併在既定預算內,擷取每個元素觀察到形狀的聯集。
在每個樣本物件中都出現的物件屬性必填(沒有 .optional())在樣本中的一致出現會被視為必填。
在至少一個樣本物件中缺少的物件屬性.optional()在任何合併樣本中缺席都會將該屬性翻轉為可選。
同一個金鑰在樣本之間持有不同型別以確定性順序的遞迴聯集保留每個觀察到的值型別,且不會在執行間重新排序。
null 與另一個觀察到的型別結合類似 z.string().nullable() 的風格null 被視為「缺席或未設定」的哨兵值,而不是獨立的型別。

合併規則是確定性的,這代表兩個人在不同機器上貼上同一個 JSON 值會看到位元完全相同的 Zod 原始碼。這個特性對程式碼審閱、差異比對與共用固定資料很重要——重複執行不會引入雜訊,且相同的綱要會在同一個輸出內被去重。

轉換器拒絕猜測的事項——以及原因

產生器不會推論字串格式、最小值、最大值、整數約束、列舉、字面值、可辨別聯集、日期、UUID、URL、電子郵件地址、品牌、強制轉型、預設值、轉換、細部修飾、記錄、元組、strict-object 行為、遞迴參考或業務規則。其理由很具體:單一樣本無法區分「2024-03-15」與一般字串,無法證明某個數字欄位永遠是整數,也無法從單一觀察中還原出封閉的列舉。拼字變化與小樣本不足以確立意圖,因此轉換器將這些決定留給你。

一旦你擁有穩定的起始綱要,以手動方式加入這些規則就很直觀。例如,在貼上一個使用者物件之後,如果你的合約保證識別字不可為空,可以將 id: z.string() 延伸加上 .min(1);若 API 文件記載使用 UUID,則加上 .uuid()。官方的 Zod API 參考文件 記載了你可以串接在基礎建構子上的每個方法,而在執行測試之前於 package.json 中釘住特定的 Zod 版本,可以讓產生的原始碼與你實際部署的執行階段保持相容。

數字帶有一個值得特別強調的注意事項:因為解析是透過 JSON.parse 進行,樣本中的每個數字都已遵循 JavaScript 數字精度。需要精確十進位語意的識別字、貨幣金額或任何值,通常應維持為字串——一旦產生器產生了 z.number(),該綱要就會接受任何數字,而不會檢查你的合約所要求的精度。

將輸出串接至真實專案

產生綱要的頁面不會載入 Zod、不會執行產生的程式碼,也不會上傳你的樣本。安裝發生在你自己的專案中,搭配一個特定釘選的 Zod 版本與所選擇的模組格式。在複製原始碼之後,請執行像是 Prettier 的格式化工具、執行 TypeScript 編譯器以確認推論出的型別與你的領域模型對齊,並就每個通過的案例與每個被拒絕的案例撰寫至少一個測試,而這些測試應取自真實的固定資料,而不是原始樣本。

請將產生的綱要視為需要審閱的起點,而非完成的合約。在你單一範例中出現的欄位,在正式環境中仍可能為可選;在樣本中缺席的值,仍可能有效;而 API 文件記載的任何約束——範圍、列舉、格式、建立時必填但更新時可選的語意——都必須透過閱讀權威文件來加入,而不是靠推論。那個審閱步驟正是「能編譯的綱要」與「能保護你的應用程式免於壞資料的綱要」之間的差別。

如果你正在權衡選項,Convert .properties Files to JSON Online 對此有詳細說明。

如果你正在權衡選項,CSV to HTML Table in Shell: Why It Breaks and a Safer Way 對此有詳細說明。