本文へスキップ

JSONスキーマからZodスキーマへの変換器 — Numio

json-schema-to-zod を使って JSON Schema から Zod の検証コードを生成します。minLength、minimum、pattern、default などの制約はチェーンした Zod メソッドになり、"required" にないプロパティには .optional() が付きます。

他チームから JSON Schema を受け取り、JSON Schema バリデーターを使わずに TypeScript のサービスで実行時検証をしたい場合。

JSON Schema

Zod Schema

こんなときに使えます

  • 他チームから JSON Schema を受け取り、JSON Schema バリデーターを使わずに TypeScript のサービスで実行時検証をしたい場合。
  • フォームの検証を Ajv などの JSON Schema バリデーターから Zod へ移行中で、出発点となる生成コードがほしい場合。

使い方

  1. JSON Schema パネルに JSON Schema を貼り付けます。
  2. 必要に応じて設定を開き、Root Schema Name(既定は "schema")を変更します。これがエクスポートされる const の名前になります。
  3. "zod" から z をインポートする生成コードをプロジェクトにコピーします。変換はブラウザ内で行われます。

JSON Schema

{
  "type": "object",
  "properties": {
    "username": { "type": "string", "minLength": 3, "maxLength": 20 },
    "age": { "type": "integer", "minimum": 18 },
    "contact": {
      "oneOf": [
        { "type": "string", "format": "email" },
        { "type": "string", "pattern": "^\\+[0-9]+$" }
      ]
    },
    "newsletter": { "type": "boolean", "default": false }
  },
  "required": ["username", "contact"]
}

Zod スキーマ

import { z } from "zod"

export const schema = z.object({
  username: z.string().min(3).max(20),
  age: z.number().int().gte(18).optional(),
  contact: z.any().superRefine((x, ctx) => {
    const schemas = [
      z.string().email(),
      z.string().regex(new RegExp("^\\+[0-9]+$")),
    ]
    const errors = schemas.reduce(
      (errors: z.ZodError[], schema) =>
        ((result) => ("error" in result ? [...errors, result.error] : errors))(
          schema.safeParse(x),
        ),
      [],
    )
    if (schemas.length - errors.length !== 1) {
      ctx.addIssue({
        path: ctx.path,
        code: "invalid_union",
        unionErrors: errors,
        message: "Invalid input: Should pass single schema",
      })
…

仕様と制限

oneOf は、例のとおり z.any().superRefine(...) に変換され、サブスキーマのうちちょうど 1 つが通ることを検査します。

$ref は解決されません。参照しているプロパティは z.any() になります。

integer には .int()、format "email" には .email()、format "date-time" には .datetime() が付きます。

additionalProperties: false は .strict() ではなく .catchall(z.never()) になります。

FAQ

null 許容の型はどう扱われますか?

"type": ["string", "null"] は z.union([z.string(), z.null()]) になり、必須でないプロパティには .optional() が付きます。

エクスポートされるスキーマの名前を変えられますか?

はい。設定で Root Schema Name を指定すると、出力は export const schema ではなく export const <name> = ... になります。

definitions と $ref には対応していますか?

いいえ。$ref を使うプロパティは z.any() になるため、変換前に参照先のスキーマをインライン展開してください。

仕様を確認する