JSON Toolkit

Без additionalProperties валидация схемы ничего не проверяет

Главное: Успешная валидация не значит чистые данные. Pydantic по умолчанию игнорирует неизвестные поля, Zod их отбрасывает, Ajv пропускает — выдуманные параметры и опечатки проходят тихо. Статья показывает три формы молчаливого принятия и даёт полные рецепты: закрывать объекты, контролировать приведение типов, подтягивать каждый уровень вложенности.

Intermediate~8 мин чтенияОпубликовано 2026-11-02Обновлено 2026-11-02
JSON с лишними полями проходит валидацию

Вывод: по умолчанию валидаторы игнорируют незаявленные поля, а не отклоняют их. Без additionalProperties: false успешная валидация доказывает лишь, что заявленные поля в порядке.

Самое опасное поведение валидатора — не ошибка, а тишина. Опечатка в ключе или выдуманный моделью параметр проходит сквозь валидацию бесшумно и взрывается в бизнес-логике.

Один: валидация, которая «прошла»

{
  "type": "object",
  "properties": {
    "order_id": { "type": "string" },
    "amount": { "type": "number" }
  },
  "required": ["order_id", "amount"]
}

Передайте {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — третий ключ с опечаткой. С настройками Ajv по умолчанию результат — успех. Лишнее поле не отклоняется и не помечается.

В спецификации JSON Schema значение additionalProperties по умолчанию true: незаявленные поля разрешены.

Два: три варианта молчания

Среда Поведение по умолчанию Последствие
Ajv (JS) пропускает грязные данные доходят до логики
Zod z.object отбрасывает поле исчезает, объект укорачивается
Pydantic v2 игнорирует (extra="ignore") то же, без следов в логах

Отбрасывание скрытнее пропуска. Обе библиотеки по умолчанию проваливаются одинаково — молча.

Три: вызовы инструментов LLM усугубляют проблему

Аргументы генерирует модель: она уверенно придумывает имена полей и передаёт "3" вместо числа. Платформы определяют параметры через JSON Schema, но по умолчанию не заставляют схему быть закрытой — strict-режим нужно включать явно, а грамматические ограничения покрывают только прямой вывод модели.

{
  "order_id": 4411,
  "refund_amount_usd": 50,
  "reason_code": "damaged",
  "urgent": "yes"
}

С закрытым объектом reason_code и urgent отклоняются, и ошибка указывает на путь /reason_code.

Четыре: как закрыть

Ajv: добавьте additionalProperties: false на узел объекта.

import Ajv from "ajv";
const ajv = new Ajv({ allErrors: true });

const schema = {
  type: "object",
  properties: { order_id: { type: "string" }, amount: { type: "number" } },
  required: ["order_id", "amount"],
  additionalProperties: false,
};

const validate = ajv.compile(schema);
validate({ order_id: "A1", amount: 50, reasn_code: "x" }); // false

Zod: z.strictObject или .strict(). Pydantic: ConfigDict(extra="forbid").

Общее правило: закрытие выполняется на каждом уровне. Каждый вложенный объект требует своего additionalProperties: false.

Пять: политика приведения типов

Lax-режим Pydantic превращает "5" в 5. Три политики: строгая (отклонять неверные типы), контролируемая (только явные ограниченные преобразования), свободная (лишь для внутренних данных). Для ненадёжных входов выбирайте первую или вторую и фиксируйте в тестах.

Шесть: чек-лист вложенности

Пункт Действие
Корневой объект additionalProperties: false
Каждый вложенный объект то же
Элементы массивов закрывать объекты в items
Enum и диапазоны добавить enum, minimum/maximum
Сгенерированные схемы дополнять вручную

Семь: после отклонения

allErrors: true показывает все поля сразу; verbose: true добавляет пути. Синтаксические проблемы ловит парсер до валидации схемы.

FAQ

Не слишком строго?

Зависит от контракта. Для своих входов — закрывать; для сторонних данных с расширениями — закрывать только ядро и выделить зону расширения.

Strip в Zod безопаснее?

Нет: он защищает нижние слои в ущерб вызывающему. Ошибки должны проявляться рано и явно.

Strict-режим платформы заменяет локальную валидацию?

Нет. Схема может быть открытой, а грамматика может не покрывать аргументы инструментов.

После закрытия старые данные массово падают?

Это именно то, что нужно было показать. Инвентаризируйте через allErrors, добавьте безвредные поля, исправьте вредные.

Решение в одну строку?

Нет, но минимальный шаг: проверить, что за каждым type: "object" следует additionalProperties: false.