1. Введение
Итак, вы уже умеете превращать объекты в JSON (и обратно). Но что делать, если на вход вашей программы попадает нечто… странное? Например, вы ожидали вот такой JSON:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
А получили:
{
"name": 42,
"id": "not a number"
}
Да, сериализаторы C# честно попробуют десериализовать этот беспредел, но зачастую это приводит к ошибкам в рантайме, потере данных или даже крошащемуся бизнесу. Перед тем как отправить данные дальше, сохранить их в базу или отправить по сети, проверьте, что данные валидны — иначе ваш код становится похожим на акробата без страховки.
Вот зачем и нужна валидация: это автоматическая проверка того, что JSON соответствует правилам — типам значений, обязательности полей, диапазонам, структуре и т. д.
2. Какие бывают способы валидации JSON?
В C# и .NET есть три главных подхода:
- Валидация по собственному коду: вручную парсим объект, пишем проверки и бросаем исключения при ошибках.
- Атрибуты валидации на моделях: например, [Required], [Range], [EmailAddress] из пространства имён System.ComponentModel.DataAnnotations.
- Валидация с помощью JSON Schema — наш сегодняшний герой!
JSON Schema — это стандарт, позволяющий формально описать, как должен выглядеть валидный документ. Схема — это тоже JSON. Вы задаёте, какие поля нужны, каких они типов, какие значения допустимы и т. п.
С помощью схем вы описываете:
- Какие поля должны присутствовать.
- Какие типы ожидаются (строка, массив, число, объект…).
- Какие поля обязательные, а какие нет.
- Диапазоны значений (например, возраст от 0 до 150).
- Маски, длины, перечни значений и др.
Пример самой простой схемы
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
3. Основные элементы JSON Schema
Давайте разберёмся на примерах, что означают основные ключи в JSON Schema.
| Ключ | Описание | Пример |
|---|---|---|
|
Ссылка на версию стандарта JSON Schema | |
|
Тип значения (object, array, string, number и др.) | |
|
Описание свойств объекта | |
|
Массив обязательных полей | |
|
Описание типа элементов массива | |
|
Перечень допустимых значений | |
, |
Ограничения для чисел | |
, |
Ограничения длины строк | |
|
Регулярное выражение для строк | |
Визуально: пример схемы для массива людей
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
4. Как провести валидацию JSON против схемы в C#
В .NET стандартной поддержки JSON Schema нет (по состоянию на .NET 9). На практике используют внешние библиотеки: NJsonSchema или Newtonsoft.Json.Schema (Json.NET Schema).
Установка пакета Newtonsoft.Json.Schema (Json.NET Schema)
Выполните в терминале (в папке проекта):
dotnet add package Newtonsoft.Json.Schema
Важно: Newtonsoft.Json.Schema — коммерческая библиотека (для некоммерческих целей доступна бесплатно). Для pet-проектов подходит идеально.
Пример проверки JSON на соответствие схеме
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
string schemaJson = @"{
'type': 'object',
'properties': {
'id': { 'type': 'integer' },
'name': { 'type': 'string' },
'email': { 'type': 'string', 'format': 'email' }
},
'required': ['id', 'name']
}";
// Представим, у нас есть такой JSON
string json = @"{
'id': 123,
'name': 'Alice',
'email': 'alice@example.com'
}";
// Сначала парсим схему
JSchema schema = JSchema.Parse(schemaJson);
// Парсим сам JSON в JToken
JToken jsonObj = JToken.Parse(json);
// Проверяем, соответствует ли JSON схеме
bool valid = jsonObj.IsValid(schema, out IList<string> errors);
if (valid)
{
Console.WriteLine("JSON валиден!");
}
else
{
Console.WriteLine("JSON НЕ валиден!");
foreach (var error in errors)
Console.WriteLine(error);
}
Как использовать проверку JSON в приложении?
Обычно: сначала валидация (IsValid), затем десериализация (JsonConvert.DeserializeObject<T>) и только после — бизнес-логика. Это отсекает мусорные данные на ранней стадии.
if (jsonObj.IsValid(schema))
{
// Всё хорошо, можно десериализовать
var person = JsonConvert.DeserializeObject<Person>(json);
}
else
{
// Прекратить обработку! Данные некорректны.
}
5. Применение JSON Schema в реальной жизни
Когда нужна валидация?
- При получении данных по API (особенно от внешних систем и разных клиентов).
- При миграции данных между микросервисами и базами.
- При генерации UI-форм и автозаполнении, если форма строится на основе схемы.
- На собеседованиях: вопрос «что если пришёл неправильный JSON?» встречается часто.
Интересный момент для любопытных
- Проверка форматов: "format": "email", "date-time".
- Составные правила: anyOf, oneOf, allOf.
- Валидация вложенных объектов и массивов.
- Расширения схем под свои нужды.
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": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
Проверка в .NET:
string jsonArray = @"[ ... ]"; // см. выше
string schemaJson = @"{ ... }"; // см. выше
JSchema schema = JSchema.Parse(schemaJson);
JToken arrayToken = JToken.Parse(jsonArray);
bool isValid = arrayToken.IsValid(schema, out IList<string> errs);
if (!isValid)
{
foreach (var error in errs)
Console.WriteLine(error);
// Примеры сообщений:
// "String 'что это?' is not a valid integer."
// "Integer 123 is not a valid string."
// "String 'not-an-email' is not a valid email address."
}
7. Типичные ошибки при работе с JSON-схемами
Ошибка №1: несоответствие схемы и актуальных данных. После изменения модели забывают обновить схему. Валидация либо пропускает ошибки, либо блокирует корректные данные.
Ошибка №2: несовпадение типов в схеме и в модели. Поле в модели сменило тип (например, id стал строкой), а в схеме остался integer — валидатор начнёт ругаться.
Ошибка №3: отсутствие обязательных для бизнеса полей. Даже если поле не отмечено как обязательное, логика может на него опираться — отсутствие приведёт к сбоям.
Ошибка №4: неверный формат данных. Поле типа string с email или датой выглядит «как строка», но может быть невалидным. Используйте format и/или дополнительные проверки.
Ошибка №5: реализация библиотеки отстаёт от стандарта. Стандарт развивается, а библиотеки — не всегда успевают. Некоторые проверки могут отсутствовать или работать иначе.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ