💎 Zod 4 is now stable!  Read the announcement.
Zod logo

JSON Schema

💎

新功能 — Zod 4 引入了一項新功能:原生 JSON Schema 轉換。JSON Schema 是描述 JSON 結構的標準(用 JSON)。它廣泛用於 OpenAPI 定義和定義 AI 的 結構化輸出

要將 Zod 架構轉換為 JSON Schema,請使用 z.toJSONSchema() 函數。

import * as z from "zod";
 
const schema = z.object({
  name: z.string(),
  age: z.number(),
});
 
z.toJSONSchema(schema)
// => {
//   type: 'object',
//   properties: { name: { type: 'string' }, age: { type: 'number' } },
//   required: [ 'name', 'age' ],
//   additionalProperties: false,
// }

所有架構和檢查都轉換為其最接近的 JSON Schema 等效項。某些類型沒有模擬,無法合理表示。有關處理這些情況的更多信息,請參閱下面的 unrepresentable(不可表示)部分。

z.bigint(); // ❌
z.int64(); // ❌
z.symbol(); // ❌
z.undefined(); // ❌
z.void(); // ❌
z.date(); // ❌
z.map(); // ❌
z.set(); // ❌
z.transform(); // ❌
z.nan(); // ❌
z.custom(); // ❌

字串格式 (String formats)

Zod 將以下架構類型轉換為等效的 JSON Schema format

// Supported via `format`
z.email(); // => { type: "string", format: "email" }
z.iso.datetime(); // => { type: "string", format: "date-time" }
z.iso.date(); // => { type: "string", format: "date" }
z.iso.time(); // => { type: "string", format: "time" }
z.iso.duration(); // => { type: "string", format: "duration" }
z.ipv4(); // => { type: "string", format: "ipv4" }
z.ipv6(); // => { type: "string", format: "ipv6" }
z.uuid(); // => { type: "string", format: "uuid" }
z.guid(); // => { type: "string", format: "uuid" }
z.url(); // => { type: "string", format: "uri" }

這些架構通過 contentEncoding 支持:

z.base64(); // => { type: "string", contentEncoding: "base64" }

所有其他字串格式通過 pattern 支持:

z.base64url();
z.cuid();
z.emoji();
z.nanoid();
z.cuid2();
z.ulid();
z.cidrv4();
z.cidrv6();
z.mac();

數字類型 (Numeric types)

Zod 將以下數字類型轉換為 JSON Schema:

// number
z.number(); // => { type: "number" }
z.float32(); // => { type: "number", exclusiveMinimum: ..., exclusiveMaximum: ... }
z.float64(); // => { type: "number", exclusiveMinimum: ..., exclusiveMaximum: ... }
 
// integer
z.int(); // => { type: "integer" }
z.int32(); // => { type: "integer", exclusiveMinimum: ..., exclusiveMaximum: ... }

對象架構 (Object schemas)

默認情況下,z.object() 架構包含 additionalProperties: "false"。這是 Zod 默認行為的準確表示,因為普通的 z.object() 架構會剝離其他屬性。

import * as z from "zod";
 
const schema = z.object({
  name: z.string(),
  age: z.number(),
});
 
z.toJSONSchema(schema)
// => {
//   type: 'object',
//   properties: { name: { type: 'string' }, age: { type: 'number' } },
//   required: [ 'name', 'age' ],
//   additionalProperties: false,
// }

"input" 模式下轉換為 JSON Schema 時,不會設置 additionalProperties。有關更多信息,請參閱 io 文檔

import * as z from "zod";
 
const schema = z.object({
  name: z.string(),
  age: z.number(),
});
 
z.toJSONSchema(schema, { io: "input" });
// => {
//   type: 'object',
//   properties: { name: { type: 'string' }, age: { type: 'number' } },
//   required: [ 'name', 'age' ],
// }

相比之下:

  • z.looseObject() 永遠不會 設置 additionalProperties: false
  • z.strictObject() 總是 設置 additionalProperties: false

文件架構 (File schemas)

Zod 將 z.file() 轉換為以下 OpenAPI 友好的架構:

z.file();
// => { type: "string", format: "binary", contentEncoding: "binary" }

大小和 MIME 檢查也得到表示:

z.file().min(1).max(1024 * 1024).mime("image/png");
// => {
//   type: "string",
//   format: "binary",
//   contentEncoding: "binary",
//   contentMediaType: "image/png",
//   minLength: 1,
//   maxLength: 1048576,
// }

可空性 (Nullability)

Zod 在 JSON Schema 中將 z.null() 轉換為 { type: "null" }

z.null();
// => { type: "null" }

請注意,z.undefined() 在 JSON Schema 中是不可表示的(見 下文)。

同樣,nullable 通過與 null 的聯合表示:

z.nullable(z.string());
// => { oneOf: [{ type: "string" }, { type: "null" }] }

可選架構按原樣表示,儘管它們帶有 optional 註釋。

z.optional(z.string());
// => { type: "string" }

配置 (Configuration)

第二個參數可用於自定義轉換邏輯。

z.toJSONSchema(schema, {
  // ...params
})

下面是每個支持參數的快速參考。每個參數在下面都有更詳細的解釋。

interface ToJSONSchemaParams {
  /** 要針對的 JSON Schema 版本。
   * - `"draft-2020-12"` — 默認。JSON Schema Draft 2020-12
   * - `"draft-7"` — JSON Schema Draft 7
   * - `"draft-4"` — JSON Schema Draft 4
   * - `"openapi-3.0"` — OpenAPI 3.0 Schema Object */
  target?: "draft-4" | "draft-7" | "draft-2020-12" | "openapi-3.0";
 
  /** 用於查找每個架構元數據的註冊表。
   * 任何具有 `id` 屬性的架構都將被提取為 $def。 */
  metadata?: $ZodRegistry<Record<string, any>>;
 
  /** 如何處理不可表示的類型。
   * - `"throw"` — 默認。不可表示的類型拋出錯誤
   * - `"any"` — 不可表示的類型變為 `{}` */
  unrepresentable?: "throw" | "any";
 
  /** 如何處理循環。
   * - `"ref"` — 默認。循環將使用 $defs 打破
   * - `"throw"` — 如果遇到循環將拋出錯誤 */
  cycles?: "ref" | "throw";
 
  /* 如何處理重用的架構。
   * - `"inline"` — 默認。重用的架構將被內聯
   * - `"ref"` — 重用的架構將被提取為 $defs */
  reused?: "ref" | "inline";
 
  /** 用於將 `id` 值轉換為 URIs 以用於 *外部* $refs 的函數。
   *
   * 默認為 `(id) => id`。
   */
  uri?: (id: string) => string;
}

target

要設置目標 JSON Schema 版本,請使用 target 參數。默認情況下,Zod 將針對 Draft 2020-12。

z.toJSONSchema(schema, { target: "draft-7" });
z.toJSONSchema(schema, { target: "draft-2020-12" });
z.toJSONSchema(schema, { target: "draft-4" });
z.toJSONSchema(schema, { target: "openapi-3.0" });

metadata

如果您還沒有,請通讀 元數據和註冊表 頁面以瞭解在 Zod 中存儲元數據的背景。

在 Zod 中,元數據存儲在註冊表中。Zod 導出一個全局註冊表 z.globalRegistry,可用於存儲常見的元數據字段,如 idtitledescriptionexamples

import * as z from "zod";
 
// `.meta()` 是一個將架構註冊到 `z.globalRegistry` 的便捷方法
const emailSchema = z.string().meta({ 
  title: "Email address",
  description: "Your email address",
});
 
z.toJSONSchema(emailSchema);
// => { type: "string", title: "Email address", description: "Your email address", ... } 

所有元數據字段都會被複製到生成的 JSON Schema 中。

const schema = z.string().meta({
  whatever: 1234
});
 
z.toJSONSchema(schema);
// => { type: "string", whatever: 1234 }

unrepresentable

以下 APIs 在 JSON Schema 中不可表示。默認情況下,如果遇到它們,Zod 將拋出錯誤。嘗試轉換為 JSON Schema 是不健全的;您應該修改您的架構,因為它們在 JSON 中沒有等效項。如果遇到其中任何一個,將拋出錯誤。

z.bigint(); // ❌
z.int64(); // ❌
z.symbol(); // ❌
z.undefined(); // ❌
z.void(); // ❌
z.date(); // ❌
z.map(); // ❌
z.set(); // ❌
z.transform(); // ❌
z.nan(); // ❌
z.custom(); // ❌

默認情況下,如果遇到其中任何一個,Zod 將拋出錯誤。

z.toJSONSchema(z.bigint());
// => throws Error

您可以通過將 unrepresentable 選項設置為 "any" 來更改此行為。這將把任何不可表示的類型轉換為 {}(JSON Schema 中的 unknown 等效項)。

z.toJSONSchema(z.bigint(), { unrepresentable: "any" });
// => {}

cycles

如何處理循環。如果 z.toJSONSchema() 在遍歷架構時遇到循環,它將使用 $ref 表示。

const User = z.object({
  name: z.string(),
  get friend() {
    return User;
  },
});
 
z.toJSONSchema(User);
// => {
//   type: 'object',
//   properties: { name: { type: 'string' }, friend: { '$ref': '#' } },
//   required: [ 'name', 'friend' ],
//   additionalProperties: false,
// }

如果相反,您想拋出錯誤,請將 cycles 選項設置為 "throw"

z.toJSONSchema(User, { cycles: "throw" });
// => throws Error

reused

如何處理在同一個架構中多次出現的架構。默認情況下,Zod 將內聯這些架構。

const name = z.string();
const User = z.object({
  firstName: name,
  lastName: name,
});
 
z.toJSONSchema(User);
// => {
//   type: 'object',
//   properties: { 
//     firstName: { type: 'string' }, 
//     lastName: { type: 'string' } 
//   },
//   required: [ 'firstName', 'lastName' ],
//   additionalProperties: false,
// }

相反,您可以將 reused 選項設置為 "ref" 以將這些架構提取到 $defs 中。

z.toJSONSchema(User, { reused: "ref" });
// => {
//   type: 'object',
//   properties: {
//     firstName: { '$ref': '#/$defs/__schema0' },
//     lastName: { '$ref': '#/$defs/__schema0' }
//   },
//   required: [ 'firstName', 'lastName' ],
//   additionalProperties: false,
//   '$defs': { __schema0: { type: 'string' } }
// }

override

要定義一些自定義的覆蓋邏輯,請使用 override。提供的回調可以訪問原始的 Zod 架構和默認的 JSON Schema。此函數應直接修改 ctx.jsonSchema

const mySchema = /* ... */
z.toJSONSchema(mySchema, {
  override: (ctx)=>{
    ctx.zodSchema; // the original Zod schema
    ctx.jsonSchema; // the default JSON Schema
 
    // directly modify
    ctx.jsonSchema.whatever = "sup";
  }
});

請注意,在調用此函數之前,不可表示的類型將拋出 Error。如果您嘗試為不可表示的類型定義自定義行為,則需要將 unrepresentable: "any"override 一起使用。

// support z.date() as ISO datetime strings
const result = z.toJSONSchema(z.date(), {
  unrepresentable: "any",
  override: (ctx) => {
    const def = ctx.zodSchema._zod.def;
    if(def.type ==="date"){
      ctx.jsonSchema.type = "string";
      ctx.jsonSchema.format = "date-time";
    }
  },
});

io

某些架構類型具有不同的輸入和輸出類型,例如 ZodPipeZodDefault 和強制原語。默認情況下,z.toJSONSchema 的結果表示 輸出類型;使用 "io": "input" 來提取輸入類型。

const mySchema = z.string().transform(val => val.length).pipe(z.number());
// ZodPipe
 
const jsonSchema = z.toJSONSchema(mySchema); 
// => { type: "number" }
 
const jsonSchema = z.toJSONSchema(mySchema, { io: "input" }); 
// => { type: "string" }

註冊表 (Registries)

將架構傳遞給 z.toJSONSchema() 將返回一個 自包含 的 JSON Schema。

在其他情況下,您可能有一組 Zod 架構,您希望使用多個相互鏈接的 JSON Schemas 來表示,也許是為了寫入 .json 文件並從 Web 服務器提供服務。

import * as z from "zod";
 
const User = z.object({
  name: z.string(),
  get posts(){
    return z.array(Post);
  }
});
 
const Post = z.object({
  title: z.string(),
  content: z.string(),
  get author(){
    return User;
  }
});
 
z.globalRegistry.add(User, {id: "User"});
z.globalRegistry.add(Post, {id: "Post"});

為了實現這一點,您可以將 註冊表 傳遞給 z.toJSONSchema()

重要 — 所有架構都應該在註冊表中具有已註冊的 id 屬性!任何沒有 id 的架構都將被忽略。

z.toJSONSchema(z.globalRegistry);
// => {
//   schemas: {
//     User: {
//       id: 'User',
//       type: 'object',
//       properties: {
//         name: { type: 'string' },
//         posts: { type: 'array', items: { '$ref': 'Post' } }
//       },
//       required: [ 'name', 'posts' ],
//       additionalProperties: false,
//     },
//     Post: {
//       id: 'Post',
//       type: 'object',
//       properties: {
//         title: { type: 'string' },
//         content: { type: 'string' },
//         author: { '$ref': 'User' }
//       },
//       required: [ 'title', 'content', 'author' ],
//       additionalProperties: false,
//     }
//   }
// }

默認情況下,$ref URIs 是像 "User" 這樣的簡單相對路徑。要使這些成為絕對 URIs,請使用 uri 選項。這需要一個將 id 轉換為完全限定 URI 的函數。

z.toJSONSchema(z.globalRegistry, {
  uri: (id) => `https://example.com/${id}.json`
});
// => {
//   schemas: {
//     User: {
//       id: 'User',
//       type: 'object',
//       properties: {
//         name: { type: 'string' },
//         posts: {
//           type: 'array',
//           items: { '$ref': 'https://example.com/Post.json' }
//         }
//       },
//       required: [ 'name', 'posts' ],
//       additionalProperties: false,
//     },
//     Post: {
//       id: 'Post',
//       type: 'object',
//       properties: {
//         title: { type: 'string' },
//         content: { type: 'string' },
//         author: { '$ref': 'https://example.com/User.json' }
//       },
//       required: [ 'title', 'content', 'author' ],
//       additionalProperties: false,
//     }
//   }
// }

On this page