1. Зачем нужна валидация JSON
Давайте представим: вы написали класс User и ожидаете, что на вход вам всегда будет приходить вот такой JSON:
{
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
Но вдруг вам прилетает вот это:
{
"id": "сорок два",
"name": 123,
"email": null,
"admin": true
}
Или вообще:
{
"username": "Alice"
}
В лучшем случае Jackson или Gson выбросят исключение при попытке десериализации. В худшем — молча присвоят полям значения по умолчанию, и ваш бизнес-код начнёт работать некорректно. А если это конфиг для вашего сервиса — можно нарваться на весёлые баги, которые потом ищут всей командой.
Валидация JSON — это процесс проверки, что структура, типы и значения данных в JSON соответствуют определённым правилам (схеме). Это как паспортный контроль для данных: не прошёл — на борт не пускаем!
2. JSON Schema: что это и как выглядит
В мире JSON есть официальный стандарт описания структуры данных — JSON Schema. Это такой «чек-лист», по которому можно проверить, что JSON подходит под требования вашей программы.
JSON Schema — это тоже JSON, только со специальными ключами: type, properties, required и так далее.
Пример самой простой схемы
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
Что здесь происходит:
- Ожидается объект (type: "object").
- У объекта могут быть поля "id", "name", "email" (описываются в properties).
- "id" — обязательно целое число (type: "integer").
- "name" — обязательно строка (type: "string").
- "email" — строка, которая выглядит как email (ключ format со значением "email").
- required указывает список обязательных полей: "id" и "name".
Если в JSON будет отсутствовать "id" или "name", или их тип будет не тот — валидация не пройдёт.
Кратко о возможностях JSON Schema
- Указание типа (type: "string", "integer", "array", "object", "boolean", "null").
- Описание вложенных объектов и массивов (properties, items).
- Обязательные и необязательные поля (required).
- Проверка длины строк, диапазона чисел (minLength, maximum и др.).
- Проверка формата (format: "email", "date", "uri", и др.).
- Перечисления (enum: список допустимых значений).
- Регулярные выражения для строк (pattern).
- Сложные условия: anyOf, oneOf, allOf (для продвинутых случаев).
3. Валидация JSON в Java: обзор библиотек
В стандартную библиотеку Java валидация JSON по схеме не входит. Но есть популярные сторонние библиотеки. Вот самые известные:
- everit-org/json-schema — простая, бесплатная и популярная.
- networknt/json-schema-validator — быстрая, поддерживает последние стандарты.
- Jackson-module-jsonSchema — расширение для Jackson (но не поддерживает полноценную валидацию).
- Justify, Java JSON Tools — есть и другие, но они встречаются реже.
В этой лекции мы рассмотрим everit-org/json-schema — она проста для новичков, хорошо документирована и не требует танцев с бубном.
Установка everit-org/json-schema
Добавьте зависимость в ваш pom.xml (Maven):
<dependency>
<groupId>org.everit.json</groupId>
<artifactId>org.everit.json.schema</artifactId>
<version>1.14.2</version>
</dependency>
Или через Gradle:
implementation 'org.everit.json:org.everit.json.schema:1.14.2'
4. Пример: валидация JSON по схеме (пошагово)
Давайте попробуем провалидировать JSON на практике. Для этого нам понадобится сама схема, JSON и немного кода.
Пример схемы (user-schema.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
Пример валидного JSON (user.json):
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
Пример невалидного JSON:
{
"id": "один",
"name": "",
"email": "not-an-email"
}
Код для валидации
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONTokener;
import org.everit.json.schema.ValidationException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class JsonValidationExample {
public static void main(String[] args) throws Exception {
// Загрузка схемы из файла
String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
Schema schema = SchemaLoader.load(rawSchema);
// Загрузка JSON для проверки
String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
JSONObject json = new JSONObject(new JSONTokener(jsonString));
// Валидация
try {
schema.validate(json); // Если всё ок — ничего не произойдет
System.out.println("JSON валиден!");
} catch (ValidationException e) {
System.out.println("JSON НЕ валиден!");
for (String msg : e.getAllMessages()) {
System.out.println("Ошибка: " + msg);
}
}
}
}
Комментарии к коду:
- Используется класс Schema и загрузчик SchemaLoader.load(...).
- Метод schema.validate(json) выбрасывает исключение при несоответствии схеме.
- В блоке catch можно получить все ошибки через getAllMessages().
Как это интегрировать в приложение?
Обычно схема хранится в ресурсе (например, в папке resources). Вы валидируете JSON до десериализации в Java-объект. Если всё хорошо — десериализуете и работаете дальше.
5. Обработка ошибок валидации
Когда JSON не проходит проверку, библиотека выбрасывает исключение ValidationException. В сообщении содержится список ошибок: что именно не соответствует схеме.
Пример вывода ошибок
Для невалидного JSON выше вывод будет примерно таким:
JSON НЕ валиден!
Ошибка: #: required key [id] not found
Ошибка: #/name: expected minLength: 2, actual: 0
Ошибка: #/email: String [not-an-email] is invalid against requested format [email]
Ошибка: #/id: expected type: Integer, found: String
Как интерпретировать ошибки:
- required key [id] not found — отсутствует обязательное поле.
- expected minLength: 2, actual: 0 — строка слишком короткая.
- String [...] is invalid against requested format [email] — некорректный email.
- expected type: Integer, found: String — тип не совпадает.
Важно! Сообщения могут быть на английском, но они очень понятны.
Как показать ошибки пользователю?
Вы можете собрать ошибки в список и отдать пользователю, например, в REST API или GUI. Это позволит быстро понять, что именно не так с входными данными.
6. Практика: валидация массива объектов
Часто приходится валидировать не один объект, а массив:
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "что это?", "name": 123, "email": "not-an-email" }
]
Схема:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
Валидация работает так же, только в качестве JSON передаём массив. Ошибки будут содержать индексы элементов, где что-то не так (например, [#/2/id]).
7. Типичные ошибки при валидации JSON
Ошибка №1: Несовпадение типов данных. Очень часто приходит строка вместо числа ("id": "123"), а схема ждёт integer. Валидация не пройдёт. Если вы не контролируете источник данных — либо исправьте схему, либо конвертируйте данные заранее.
Ошибка №2: Отсутствие обязательных полей. Если в схеме указано, что поле обязательно ("required": ["id","name"]), а в JSON его нет — получите ошибку. Иногда это бывает неожиданно: фронтенд забыл отправить поле или API изменилось.
Ошибка №3: Лишние поля в JSON. По умолчанию JSON Schema разрешает лишние поля. Если вы хотите строгую схему, не забудьте добавить "additionalProperties": false. Без этого в JSON могут быть любые «левые» поля.
Ошибка №4: Неправильная версия схемы или синтаксис. Если вы используете ключи, которых нет в вашей версии схемы, или делаете опечатки, валидатор не сможет загрузить схему. Проверяйте схему на https://www.jsonschemavalidator.net/ или аналогичных сервисах.
Ошибка №5: Плохая обработка ошибок. Если просто ловить первое исключение и не показывать пользователю подробности, будет сложно понять, что именно не так с JSON. Используйте getAllMessages() для вывода всех ошибок.
Ошибка №6: Слишком строгие форматы. Проверка "format": "email" или "date" бывает довольно «поверхностной». Если вам нужна строгая валидация, используйте дополнительные проверки в коде.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ