Схемы из JSON Schema

JSON Schema представляет декларативную модель описания структуры данных, тогда как Zod строится как композиционная система валидаторов в JavaScript/TypeScript. При сопоставлении этих подходов ключевым становится различие в философии: JSON Schema ориентирована на описание контракта данных, Zod — на выполнение валидации во время исполнения с возможностью преобразований и строгой типизации.

Базовые соответствия выглядят прямолинейно:

  • stringz.string()
  • numberz.number()
  • integerz.number().int()
  • booleanz.boolean()
  • nullz.null()
  • anyz.any()

Дополнительные ограничения JSON Schema транслируются через методы Zod:

  • minLength, maxLengthz.string().min().max()
  • minimum, maximumz.number().min().max()
  • patternz.string().regex()
  • multipleOf → пользовательская проверка через refine

Объекты и структура данных

JSON Schema описывает объекты через type: "object", properties, required и additionalProperties.

В Zod аналогичная модель выражается через z.object({...}), где ключи объекта определяются явно.

Обязательные и опциональные поля

В JSON Schema обязательные поля задаются массивом required. В Zod обязательность определяется наличием .optional().

// JSON Schema
{
  type: "object",
  properties: {
    id: { type: "string" },
    age: { type: "number" }
  },
  required: ["id"]
}
const schema = z.object({
  id: z.string(),
  age: z.number().optional()
});

Дополнительные свойства

JSON Schema использует additionalProperties, тогда как Zod управляет этим через .strict(), .passthrough() и .strip():

  • strict() — запрещает лишние поля
  • passthrough() — сохраняет лишние поля
  • strip() — удаляет неизвестные поля (поведение по умолчанию)

Массивы и элементы

В JSON Schema массивы описываются через type: "array" и items. Zod использует z.array().

// JSON Schema
{
  type: "array",
  items: { type: "string" }
}
const schema = z.array(z.string());

Ограничения:

  • minItems.min()
  • maxItems.max()
  • uniqueItems.refine(arr => new Set(arr).size === arr.length)

Перечисления и константы

JSON Schema:

  • enum — фиксированный набор значений
  • const — строго одно значение

Zod:

  • z.enum([...])
  • z.literal(value)
const schema = z.enum(["small", "medium", "large"]);
const constant = z.literal("fixed-value");

Комбинирование схем: oneOf, anyOf, allOf

JSON Schema предоставляет логические операторы композиции:

  • oneOf
  • anyOf
  • allOf

В Zod аналог реализуется через:

  • z.union([...])
  • z.intersection(a, b)
  • цепочки .and() и .or() в некоторых случаях

oneOf → union с дополнительной дискриминацией

const schema = z.union([
  z.object({ type: z.literal("a"), value: z.string() }),
  z.object({ type: z.literal("b"), count: z.number() })
]);

allOf → intersection

const schema = z.intersection(
  z.object({ id: z.string() }),
  z.object({ timestamp: z.number() })
);

Рекурсия и ссылки $ref

JSON Schema активно использует $ref для повторного использования и рекурсивных структур.

Zod реализует рекурсию через z.lazy():

const Node = z.lazy(() =>
  z.object({
    value: z.string(),
    children: z.array(Node).optional()
  })
);

Прямого аналога $ref нет, но концептуально z.lazy выполняет ту же роль отложенного разрешения структуры.

Значения по умолчанию и трансформации

JSON Schema поддерживает default, но не определяет поведение выполнения. Zod интегрирует значение по умолчанию и трансформации в саму модель:

z.string().default("value");

Трансформации:

z.string().transform(val => val.trim());

Дополнительно:

  • preprocess() используется для нормализации входных данных до валидации
  • transform() изменяет результат после проверки

Различия в строгой типизации

JSON Schema допускает слабую интерпретацию типов (например, число как строка в некоторых реализациях). Zod строго следует runtime-проверкам и TypeScript-инференсу.

Особенности:

  • Zod всегда выполняет проверку во время исполнения
  • JSON Schema может использоваться декларативно без выполнения

Генерация JSON Schema из Zod

Существует экосистема инструментов, наиболее распространённый — zod-to-json-schema.

Механизм преобразования:

  • Zod-схема анализируется AST-подобно
  • Формируется JSON Schema Draft-совместимый объект

Пример:

import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const schema = z.object({
  id: z.string(),
  count: z.number().int()
});

const jsonSchema = zodToJsonSchema(schema);

Ограничения генерации:

  • сложные transform() теряются
  • refine() не всегда выражается стандартами JSON Schema
  • рекурсивные структуры требуют дополнительной настройки
  • union/intersection могут упрощаться

Обратное преобразование JSON Schema в Zod

Автоматическое преобразование JSON Schema → Zod сложнее из-за различий в выразительности.

Основные сложности:

  • anyOf без дискриминатора требует сложной логики union
  • patternProperties не имеет прямого аналога
  • additionalProperties может конфликтовать с .strict()
  • $ref требует предварительного разрешения графа схем

При генерации Zod-схем из JSON Schema обычно:

  • строится промежуточное представление
  • разрешаются ссылки
  • упрощаются union/intersection конструкции

Ограничения взаимной совместимости

Несовпадения моделей приводят к потере информации при конвертации:

  • Zod трансформации не представимы в JSON Schema
  • JSON Schema валидационные ключи не всегда выражаются в Zod без кастомных refine
  • format в JSON Schema требует ручной интерпретации (email, uri, date-time)

Пример:

z.string().email();

соответствует:

{ "type": "string", "format": "email" }

но обратная интерпретация зависит от генератора.

Строгость и поведение дополнительных данных

JSON Schema:

  • additionalProperties: false — строгий режим
  • true — разрешение любых полей

Zod:

  • .strict() — запрет
  • .passthrough() — разрешение
  • .strip() — удаление

Различие проявляется в том, что Zod всегда определяет поведение на уровне рантайма, тогда как JSON Schema зависит от интерпретатора.

Практические модели совместного использования

В типичных архитектурах Zod используется как слой runtime-валидации, а JSON Schema — как контракт для внешних систем.

Комбинированный подход:

  • Zod как источник истины
  • JSON Schema как экспортируемый формат для API-документации

Или обратный сценарий:

  • JSON Schema как спецификация API
  • Zod как слой исполнения и проверки

Особенности работы с форматами и расширениями

JSON Schema расширяется через format и кастомные ключи $defs. Zod использует методы и композицию.

Сопоставление:

  • date-timez.string().datetime() (или refine)
  • uuidz.string().uuid()
  • emailz.string().email()

При отсутствии встроенного аналога используется:

z.string().refine(val => customCheck(val));

Производительность и структура проверки

JSON Schema-движки часто оптимизируют проверки через заранее скомпилированные схемы. Zod выполняет цепочку валидаторов, создаваемых при объявлении схемы.

Особенность Zod:

  • каждая схема — функция проверки
  • композиция приводит к вложенным вызовам

Особенность JSON Schema:

  • возможна генерация оптимизированного валидатора
  • часто используется валидация через кодогенерацию

Итоговые различия моделей представления

Zod и JSON Schema пересекаются в области описания структур, но различаются на уровне исполнения:

  • JSON Schema — декларативное описание контракта данных
  • Zod — исполняемая система типов и валидации с возможностью трансформаций
  • конвертация между ними всегда частично теряет семантику
  • наиболее стабильная зона совместимости — базовые примитивы, объекты, массивы и простые union-конструкции