Если вы писали HTTP-сервисы на 1С, вы знаете этот момент. JSON пришёл, ПрочитатьJSON отработал, и начинается самое интересное - убедиться, что данные вообще похожи на то, что ожидалось.
Обычно это выглядит так:
Если НЕ ЗначениеЗаполнено(Данные["clientId"]) Тогда
Возврат ОшибкаФормата("Не заполнен clientId");
КонецЕсли;
Если ТипЗнч(Данные["amount"]) <> Тип("Число") Тогда
Возврат ОшибкаФормата("amount должен быть числом");
КонецЕсли;
Если НЕ (Данные["amount"] > 0) Тогда
Возврат ОшибкаФормата("amount должен быть больше нуля");
КонецЕсли;
// ... и ещё 20 таких же проверок
Поначалу терпимо. Но потом добавляется новое поле - и нужно не забыть написать проверку. Поле переименовывается - нужно найти все места. Появляются вложенные объекты, массивы, условные поля. Код пухнет, а логика того, какой формат вообще ожидается, растворяется в десятках Если.
И стоит пропустить хотя бы одну проверку - клиент получает ошибку 500 с внутренним текстом платформы вместо понятного ответа. После этого начинается разбор: то ли пришли неправильные данные, то ли в коде обращение к полю, которого вообще не должно быть в запросе.
Самое неприятное - такой код не документирует контракт. Чтобы понять, что именно принимает сервис, нужно читать всю процедуру валидации целиком.
Описать структуру один раз
Есть стандарт для описания структуры JSON-документов - JSON Schema. Схема сама является JSON-объектом, в котором вы декларативно указываете: какие поля обязательны, какие типы допустимы, каковы ограничения на значения.
Вот как выглядит схема для той же заявки:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["clientId", "amount"],
"properties": {
"clientId": { "type": "string" },
"amount": { "type": "number", "exclusiveMinimum": 0 }
},
"additionalProperties": false
}
Это заменяет весь тот код выше. Плюс схему можно отдать внешней системе как документацию - она машиночитаема и понятна без объяснений.
Проблема одна: в 1С нет встроенного инструмента, который умеет валидировать JSON по такой схеме.
jsonschema-1c
Чтобы закрыть этот пробел, была написана внешняя компонента - jsonschema-1c. Она предоставляет три основных метода:
- УстановитьСхему - задаёт схему для валидации
- ДобавитьСхему - регистрирует вспомогательную схему для $ref
- Проверить - проверяет переданный JSON на соответствие схеме с описанием ошибок
- Действителен - проверяет переданный JSON на соответствие схеме без описания подробностей ошибки
Исходный код, актуальная документация и релизы: https://github.com/Toveal/jsonschema-1c
Как это выглядит на практике
Возьмём HTTP-сервис, который принимает заявку на перевод денег. Определяем схему:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["requestId", "clientInn", "amount", "currency", "createdAt"],
"properties": {
"requestId": { "type": "string", "minLength": 1 },
"clientInn": { "type": "string", "format": "ru-inn-individual" },
"amount": { "type": "number", "exclusiveMinimum": 0, "maximum": 1000000 },
"currency": { "type": "string", "enum": ["RUB", "KZT", "USD"] },
"createdAt": { "type": "string", "format": "local-date-time" },
"comment": { "type": "string", "maxLength": 500 }
},
"additionalProperties": false
}
Обработчик запроса становится простым и читаемым:
Функция ОбработатьЗапрос(Запрос)
КомпонентаПодключена = ПодключитьВнешнююКомпоненту(ПутьККомпоненте(), "JsonСхема",
ТипВнешнейКомпоненты.Native, ТипПодключенияВнешнейКомпоненты.НеИзолированно);
Если Не КомпонентаПодключена Тогда
Возврат СформироватьОтвет(500, "Ошибка подключения компоненты");
КонецЕсли;
Компонента = Новый("AddIn.JsonСхема.JsonSchema1C");
Компонента.УстановитьОсновнуюСхему(ОсновнаяСхема());
БуферОшибок = "";
Если Не Компонента.Проверить(ТестовыйJSON(), БуферОшибок) Тогда
Возврат СформироватьОтвет(400, БуферОшибок);
КонецЕсли;
// Данные корректны - работаем дальше
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТелоЗапроса);
Данные = ПрочитатьJSON(ЧтениеJSON);
// ... обработка заявки
Возврат СформироватьОтвет(200, "OK");
КонецФункции
Если документ не прошёл проверку, переменная БуферОшибок будет содержать конкретные пути и описания:
[
"-500 is less than or equal to the minimum of 0",
"\"12345\" is not a \"ru-inn-individual\"",
"\"2024-03-15T10:30:00Z\" is not a \"local-date-time\"",
"\"EUR\" is not one of \"RUB\", \"KZT\" or \"USD\"",
"\"\" is shorter than 1 character",
"Additional properties are not allowed ('extraField' was unexpected)"
]
Это можно сразу отдавать вызывающей системе - никакой дополнительной обработки не нужно.
Кастомные форматы для 1С
Компонента добавляет несколько форматов, которых нет в стандартах, но которые часто нужны в российских и казахстанских проектах.
ru-inn-individual и ru-inn-legal-entity - валидация российского ИНН по контрольным суммам, для физлиц и организаций соответственно. kz-iin - аналог для казахстанского ИИН.
local-date-time когда 1С сериализует дату через ЗаписатьJSON, платформа выдаёт строки вида "2024-03-15T10:30:00" - без часового пояса. Стандартный формат date-time такие строки не принимает, поэтому корректно сериализованные даты не прошли бы валидацию. local-date-time решает именно эту проблему.
{
"properties": {
"ИННФизлица": { "type": "string", "format": "ru-inn-individual" },
"ИННОрганизации": { "type": "string", "format": "ru-inn-legal-entity" },
"ДатаСоздания": { "type": "string", "format": "local-date-time" }
}
}
Составные схемы
Реальные схемы часто переиспользуют общие определения - адрес, контактное лицо, сумма. В JSON Schema для этого есть $ref. Компонента поддерживает его через ДобавитьСхему: сначала регистрируются все зависимости, затем устанавливается основная схема.
Компонента.ДобавитьСхему("https://myapp.local/schemas/address", ПолучитьМакет("СхемаАдреса").ПолучитьТекст());
Компонента.УстановитьСхему(ПолучитьМакет("СхемаЗаказа").ПолучитьТекст());
Порядок важен: зависимости регистрируются до установки основной схемы.
Когда это имеет смысл
Компонента хорошо работает там, где JSON приходит снаружи и его структура заранее известна: HTTP-сервисы, интеграции с внешними системами. Схема в этих случаях решает сразу две задачи - и проверяет данные, и документирует контракт.
Если у вас одно-два поля и структура никогда не меняется - проще написать проверки руками. Но как только валидация начинает разрастаться, декларативная схема даёт плоды.
Исходный код открыт : https://github.com/Toveal/jsonschema-1c
Полезные ресурсы
https://www.jsonschemavalidator.net/ - онлайн валидация JSON по схеме
https://json.ophir.dev/ - онлайн редактор JSON схемы
https://json-schema.org/learn/getting-started-step-by-step - официальный гайд по JSON Schema с нуля
https://json-schema.org/understanding-json-schema - подробный справочник по всем ключевым словам схемы
//infostart.ru/1c/articles/1543922/ - серия статей "Понимание схемы JSON"
Вступайте в нашу телеграмм-группу Инфостарт