Перейти к содержимому

Преобразование схемы JSON в схему Zod — Numio

Генерирует код валидации Zod из JSON Schema с помощью json-schema-to-zod. Ограничения вроде minLength, minimum, pattern и default становятся цепочками методов Zod, а свойства, не перечисленные в "required", получают .optional().

Вы получили JSON Schema от другой команды и хотите валидацию во время выполнения в сервисе на TypeScript без валидатора JSON Schema.

JSON Schema

Zod Schema

Когда пригодится

  • Вы получили JSON Schema от другой команды и хотите валидацию во время выполнения в сервисе на TypeScript без валидатора JSON Schema.
  • Вы переводите валидацию форм с валидатора JSON Schema, например Ajv, на Zod и хотите начать со сгенерированного кода.

Как пользоваться

  1. Вставьте JSON Schema в панель JSON Schema.
  2. При желании откройте настройки и измените Root Schema Name (по умолчанию "schema"); это имя экспортируемой const.
  3. Скопируйте сгенерированный код, импортирующий z из "zod", в свой проект. Конвертация выполняется в браузере.

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(...), который проверяет, что проходит ровно одна подсхема, как показано в примере.

$ref не разрешается: свойство со ссылкой становится z.any().

integer добавляет .int(), format "email" — .email(), а format "date-time" — .datetime().

additionalProperties: false превращается в .catchall(z.never()), а не в .strict().

FAQ

Как обрабатываются nullable-типы?

"type": ["string", "null"] становится z.union([z.string(), z.null()]) с добавлением .optional(), если свойство не обязательное.

Можно ли переименовать экспортируемую схему?

Да. Задайте Root Schema Name в настройках; тогда результат будет export const <name> = ... вместо export const schema.

Поддерживаются ли definitions и $ref?

Нет. Свойство с $ref становится z.any(), поэтому перед конвертацией встройте схему, на которую идёт ссылка.

Спецификации и справка