Как обрабатывать валидацию JSON схемы с большим количеством полей и вложенных объектов
Что проверяют
Понимаете ли вы, как держать большую схему поддерживаемой и отдавать клиенту понятные ошибки, а не одно «invalid JSON». Следом обычно спрашивают, чем валидация по схеме отличается от Bean Validation на DTO и когда какую выбрать.
Как отвечать
Начните с библиотеки. Живые варианты для Java: networknt/json-schema-validator поддерживает драфты от 4 до 2020-12 и работает поверх Jackson. Популярный в старых статьях fge/json-schema-validator давно заброшен и понимает только старые драфты 3 и 4, так что схему с "$schema": "...draft-07..." он проверит неверно.
Дальше каркас:
- декомпозиция схемы: вложенные объекты выносятся в
$defs(в draft-07 этоdefinitions) или в отдельные файлы и подключаются через$ref; - компиляция один раз: схема загружается при старте и переиспользуется между запросами. Компиляция на каждый запрос дороже самой проверки;
- все ошибки сразу: валидатор возвращает набор сообщений, у каждого путь до поля в виде JSON Pointer, например
/address/city. Их стоит отдать клиенту списком, в духе RFC 9457 (Problem Details); - лишние поля: по умолчанию схема их пропускает, опечатку в имени ловит только
additionalProperties: false. При совмещении схем черезallOfнуженunevaluatedProperties(с 2019-09); format: в драфтах 2019-09 и 2020-12 это по умолчанию аннотация, проверкаemailилиdate-timeможет потребовать отдельной настройки.
Если контракт внутренний и живёт только в Java, часто проще Bean Validation: @Valid на вложенных полях DTO, @NotNull, @Size. Схема выигрывает, когда один контракт делят сервисы на разных языках. В автотестах API схему проверяют через matchesJsonSchemaInClasspath из модуля REST Assured.
Где спотыкаются
Останавливаются на первой ошибке и возвращают клиенту одну. Клиент чинит поле и получает следующую, и так по кругу.
Не сверяют версию драфта в $schema с тем, что умеет библиотека. Часть ключевых слов молча игнорируется, и валидация проходит там, где не должна.
Путают валидацию с десериализацией. Jackson сам по себе схему не проверяет, а в Spring Boot у ObjectMapper отключён FAIL_ON_UNKNOWN_PROPERTIES, и лишние поля проходят молча.