Валидация JSON через XDTO (включая массивы)

28.08.23

Разработка - Механизмы платформы 1С

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

К сожалению, возможности загрузки какой-нибудь schema для json файликов нам не подвезли. Зато подвезли для xml. XDTO пакеты, официальная документация тут, отличная статья по пакетам тут. И по счастливому стечению обстоятельств, платформа дает нам возможность прочитать json файл в xdto пакет, как бы это безумно ни звучало (напомню, что буква x в названии xdto означает xml). А раз мы можем прочитать json в xdto, значит, мы можем прочитать его в "определенный" xdto, в котором мы заранее установили все необходимые настройки - возможность наличия пустых/необязательных полей, количество элементов в поле и прочее. 

Итак, возьмем какой-нибудь файлик в формате json:

{
   "Date": "2023-01-01T00:00:00",
   "Number": 123,
   "Name": "Некоторое имя"
}

И попробуем преобразовать его в XDTO объект:

Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Строка); // Строка - наш JSON в виде строки
Данные = ФабрикаXDTO.ПрочитатьJSON(Чтение); 
Чтение.Закрыть();

Если открыть объект, можно увидеть следующую картину:

 

 

Число преобразовалось в число, строка в строку, а дата... Дата тоже преобразовалась в строку, потому что по умолчанию десериализатор вообще не в курсе, что мы там к чему хотим преобразовать. Чтобы подсказать, что же мы ожидаем в каждом поле, надо создать XDTO-пакет. 

Для этого идем в конфигуратор, создаем новый пакет и задаем ему URI пространства имен. Вообще в этом поле может быть абсолютно любое строковое значение, но есть чисто нормативное правило задавать его в форме URL (в примере я буду использовать "http://www.is_test.kz"). В новом пакете создаем тип объекта (назовем его "ISTest") и добавляем свойства, описанные в JSON. Должно получиться как-то так

 

 

Для каждого свойства установим тип. Для этого нужно выбрать свойство, и справа на палитре свойств (привет тавтология) открываем список доступных типов. Нам нужен пакет "http://www.w3.org/2001/XMLSchema" который находится в самом низу. Для поля date в пакете находим тип dateTime, для поля number ищем тип int, и для поля name устанавливаем тип string. Теперь нам нужно подправить код чтения JSON, передав ему наш новый XDTO-пакет, в который мы хотим этот самый JSON прочитать

Тип = ФабрикаXDTO.Тип("http://www.is_test.kz", "ISTest");

Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Строка);
Данные = ФабрикаXDTO.ПрочитатьJSON(Чтение, Тип); 
Чтение.Закрыть();

На выходе в переменной "Данные" мы увидим это

 

 

Отлично, дата теперь преобразовывается в дату. И хорошо, если дата приходит нам в таком формате "2023-01-01T00:00:00". Но ведь системы бывают разные, интеграции бывают разные, и дату вам могут отправить в совершенно любом варианте. Давайте поменяем наш JSON файл:

{
   "Date": "10.09.1993 15:00:00",
   "Number": 123,
   "Name": "Некоторое имя"
}

При выполнении преобразование в наш XDTO объект мы отхватим ошибку:  

"Ошибка при вызове метода контекста (ПрочитатьJSON): Несоответствие типов XDTO: Ошибка проверки данных XDTO:
Значение: '10.09.1993 15:00:00' не соответствует простому типу: {http://www.w3.org/2001/XMLSchema}dateTime"

Это значит, что если мы должны работать с этим полем, как с датой, но сторонняя система может отправлять дату в "своем личном независимом" формате, мы должны обучить нашу функцию приводить это значение к нужному типу. Помощником в преобразовании (в терминах платформы и далее по тексту - восстановление) является третий, четвертый, пятый, шестой и седьмой параметры метода "ПрочитатьJSON" с названиями "ИмяФункцииВосстановления", "МодульФункцииВосстановления", "ДополнительныеПараметры, "ТипыДляОбработкиВосстановления" и "ИменаСвойствДляВосстановления" соответственно. Все они необязательные.

ИмяФункцииВосстановления - строка - имя функции, которую мы будем вызывать. На вход функция должна принимать четыре параметра - "Свойство", "Тип", "Значение", ДополнительныеПараметры", возвращать функция должна объект, допустимый для XDTO сериализации.

МодульФункцииВосстановления - нужно передать модуль, в котором будет описана наша функция восстановления. В роли модуля может выступать общий неглобальный модуль, форма клиентского приложения или команда командного интерфейса.

ДополнительныеПараметры - произвольный - любое значение для передачи в функцию восстановления

ТипыДляОбработкиВосстановления - массив, в массиве передаем типы объектов XDTO, которые нужно обработать через функцию восстановления

ИменаСвойствДляВосстановления - массив, строковые значения (я так и не смог передать сюда массив с именами полей, почему то происходит аварийная ошибка и закрывается режим предприятия)

А еще нам нужно создать свой тип, который на прием может получить строку, а в процессе восстановления приведет это значение к типу "дата". Вернемся к нашему XDTO-пакету и создадим там тип значения. В типе значений добавим новый тип и назовем его "dateTime". В новосозданном типе найдем свойство "Типы объединения" и укажем там 2 значения, dateTime и string из пакета "http://www.w3.org/2001/XMLSchema"

 

 

И теперь в нашем объекте "ISTest" в поле "Date" выберем тип dateTime из нашего пакета "http://www.is_test.kz"

 

 

Ну и осталось написать код для восстановления. Для начала нам надо показать, какие типы мы будем восстанавливать

ТипыВосстановления = Новый Массив();
ТипыВосстановления.Добавить(ФабрикаXDTO.Тип("http://www.is_test.kz", "dateTime"));

И передать нужные параметры в метод

Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(JSON);
Данные = ФабрикаXDTO.ПрочитатьJSON(
	Чтение, 
	ТипФабрики, 
	"ВосстановлениеСвойств", // имя функции восстановления
	ВалидацияJSON,        // модуль, в котором находится функция
	,                     // тут могут быть дополнительные параметры, которые полетят в функцию восстановления
	ТипыВосстановления    // массив типов XDTO, которые необходимо восстановить
); 
Чтение.Закрыть();

И напишем саму функцию восстановления

Функция ВосстановлениеСвойств(Свойство, Тип, Значение, ДополнительныеПараметры) Экспорт  
	Фабрика = Значение.Фабрика();
	Если НРег(Свойство) = "date" Тогда
		Попытка
			Дата = Дата(Значение.Значение); 
		Исключение             
			ТекстОшибки = НСтр("ru = 'В поле %1 должно быть значение с типом Дата в формате дд.ММ.гггг ЧЧ:мм:сс'");
			ВызватьИсключение СтрШаблон(ТекстОшибки, Свойство); 
		КонецПопытки;
		
		Возврат Фабрика.Создать(Тип, Дата);
	КонецЕсли;
КонецФункции

В функции сначала мы получаем фабрику XDTO для создания нужного значения. Потом мы проверяем, а то ли поле мы сейчас собираемся восстанавливать (В параметр "Свойство" прилетает строка с именем поля).  Потом идет само восстановление. Так как мы знаем, в каком формате нам должна прилететь дата, мы можем написать нужный код для восстановления. Если же в процессе вылетит исключение, обрабатываем его и отправляем вверх по стеку. В итоге мы создаем в фабрике значение с нужным типом (тип поля прилетает в параметр "Тип"), и устанавливаем полю наше восстановленное значение.

Проверяем:

 

 

Поле мы получили в формате даты. Отлично. Усложним задачу. 

При интеграции мы хотим проверять, что поле "Date" должно быть заполнено обязательно, поле "Number" может быть пустым, а поле "Name" может быть во входящем файле, а может и не быть, но если поле в файле есть, оно должно быть заполнено. Для этого нам надо вернуться в наш XDTO-пакет. 

Открывая палитру свойств полей нужно установить следующее:

Поле "Date" - свойство "Возможно пустое" - ложь, свойство "Минимальное количество" - 1

Поле "Number" - свойство "Возможно пустое" - истина, свойство "Минимальное количество" - 1

Поле "Name" - свойство "Возможно пустое" - ложь, свойство "Минимальное количество" - 0

Теперь если в поле "Number" вы захотите передать 0, или пустую строку, XDTO преобразует это как 0, если передать null, XDTO преобразует это как Неопределено. Но если в файле не будет поля "Number", то XDTO выдаст не очень понятную ошибку, но в ней будут такие строки:

Структура объекта не соответствует типу: {http://www.is_test.kz}ISTest
Не установлено значение одного из следующих свойств: Number

Далее, если из файла JSON мы удалим поле "Name", то получим вот такие данные:

 

 

То есть в самом XDTO объекте поле останется, но ему будет присвоено значение Неопределено.

Но, если мы передадим пустую строку:

 

 

То никакой ошибки не будет. Объект спокойно проглотит пустую строку, хотя мы устанавливали в свойствах, что если поле "Name" есть, оно должно быть заполнено. Это особенности работы с этим странным механизмом. Но мы умеем внедряться в восстановление полей, а значит можем вручную обработать пустую строку.

Добавляем новый тип для восстановления

ТипыВосстановления = Новый Массив();
ТипыВосстановления.Добавить(ФабрикаXDTO.Тип("http://www.is_test.kz", "dateTime"));
ТипыВосстановления.Добавить(ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "string"));

И добавляем кода в метод "ВосстановлениеСвойств"

Функция ВосстановлениеСвойств(Свойство, Тип, Значение, ДополнительныеПараметры) Экспорт  
	Фабрика = Значение.Фабрика();
	Если НРег(Свойство) = "date" Тогда		
		Попытка
			Дата = Дата(Значение.Значение); 
		Исключение             
			ТекстОшибки = НСтр("ru = 'В поле %1 должно быть значение с типом Дата в формате дд.ММ.гггг ЧЧ:мм:сс'");
			ВызватьИсключение СтрШаблон(ТекстОшибки, Свойство); 
		КонецПопытки;
		
		Возврат Фабрика.Создать(Тип, Дата);   
		
	ИначеЕсли НРег(Свойство) = "name" Тогда 
		Если Не ЗначениеЗаполнено(Значение.Значение) Тогда 
			ТекстОшибки = НСтр("ru = 'Поле %1 должно быть заполнено'");
			ВызватьИсключение СтрШаблон(ТекстОшибки, Свойство); 
		КонецЕсли;   
		
		Возврат Фабрика.Создать(Тип, Значение.Значение);   
		
	КонецЕсли;
КонецФункции

Теперь при обработке файла:

{
   "Date": "10.09.1993 15:00:00",
   "Number": 123,
   "Name": ""
}

Мы получим ошибку:

Поле Name должно быть заполнено

Ну вроде все, мы разобрались с примитивными типами. Но! В JSON есть еще такое понятие как массив.Обработать мы его можем, но с кучей ограничений. Мы не можем просто взять и передать поле в XDTO-пакет

{"nums": [1, 2, 3]}

Оно просто не обработается. Но мы можем передать массив объектов

{"Objects": [
   {
      "Date": "10.09.1993 15:00:00",
      "Number": 1,
      "Name": "Первый",
   },
   {
      "Date": "10.09.1993 15:00:00",
      "Number": 2,
      "Name": "Второй",
   },
   {
      "Date": "10.09.1993 15:00:00",
      "Number": 3,
      "Name": "Третий",
   }
]}

Заведем новое свойство для нашего объекта "ISTest", назовем его "Objects", базовый тип устанавливать ему не будем. В палитре свойств  установим полю "Максимальное количество" значение 5. Максимальное количество в этом случае будет определять максимальную длину массива.

Получится примерно так:

 

 

Ну и, конечно, для него работают все наши предыдущие правила. Например, такой файл обработается нормально:

{"Objects": [
   {
      "Date": "10.09.1993 15:00:00",
      "Number": null,
      "Name": "Первый",
   },
   {
      "Date": "10.09.1993 15:00:00",
      "Number": 0
   },
   {
      "Date": "10.09.1993 15:00:00",
      "Number": 3,
      "Name": "Третий",
   }
]}

Все, что остается сделать, это закидывать входящие файлы в XDTO-пакет, если вылетит исключение, файл невалиден и работать с ним смысла нет, если же файл обработается нормально, можно принимать файл и отправлять в дальнейшую обработку. 

Вот такие возможности нам предлагает валидация через XDTO пакеты. Никого не агитирую валидировать JSON именно так, только вам решать как обрабатывать входящие данные. Но знать такой механизм, как XDTO-пакеты, нужно. 

 
 Текст модуля и XDTO-пакет

 

Полный код итогового модуля:

Функция ВалидацияJSONчерезXDTO(JSON) Экспорт
	ТипФабрики = ФабрикаXDTO.Тип("http://www.is_test.kz", "ISTest");
	ТипыВосстановления = Новый Массив();
	ТипыВосстановления.Добавить(ФабрикаXDTO.Тип("http://www.is_test.kz", "dateTime"));
	ТипыВосстановления.Добавить(ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "string"));
	
	Попытка
		Чтение = Новый ЧтениеJSON;
		Чтение.УстановитьСтроку(JSON);
		Данные = ФабрикаXDTO.ПрочитатьJSON(
			Чтение, 
			ТипФабрики, 
			"ВосстановлениеСвойств", 
			ВалидацияJSON, 
			, 
			ТипыВосстановления			
		); 
		Чтение.Закрыть();
	Исключение
		ТекстОшибки = НСтр("ru = 'При обработке входящего файла произошла ошибка: %1%2'");
		Ошибка = ОписаниеОшибки();  
		ПолныйТекстОшибки = СтрШаблон(ТекстОшибки, Символы.ПС, Ошибка);

		ВызватьИсключение ПолныйТекстОшибки;
	КонецПопытки;	
КонецФункции

Функция ВосстановлениеСвойств(Свойство, Тип, Значение, ДополнительныеПараметры) Экспорт  
	Фабрика = Значение.Фабрика();
	Если НРег(Свойство) = "date" Тогда		
		Попытка
			Дата = Дата(Значение.Значение); 
		Исключение             
			ТекстОшибки = НСтр("ru = 'В поле %1 должно быть значение с типом Дата в формате дд.ММ.гггг ЧЧ:мм:сс'");
			ВызватьИсключение СтрШаблон(ТекстОшибки, Свойство); 
		КонецПопытки;
		
		Возврат Фабрика.Создать(Тип, Дата);   
		
	ИначеЕсли НРег(Свойство) = "name" Тогда 
		Если Не ЗначениеЗаполнено(Значение.Значение) Тогда 
			ТекстОшибки = НСтр("ru = 'Поле %1 должно быть заполнено'");
			ВызватьИсключение СтрШаблон(ТекстОшибки, Свойство); 
		КонецЕсли;   
		
		Возврат Фабрика.Создать(Тип, Значение.Значение);   
		
	КонецЕсли;
КонецФункции

XDTO-пакет (сохранить в файл с расширение xsd и импортировать в пакет в конфигураторе):

<xs:schema xmlns:tns="http://www.is_test.kz" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.is_test.kz" attributeFormDefault="unqualified" elementFormDefault="qualified">
	<xs:simpleType name="dateTime">
		<xs:union memberTypes="xs:string xs:dateTime"/>
	</xs:simpleType>
	<xs:complexType name="ISTest">
		<xs:sequence>
			<xs:element name="Objects" maxOccurs="5">
				<xs:complexType>
					<xs:sequence>
						<xs:element name="Date" type="tns:dateTime"/>
						<xs:element name="Number" type="xs:int" nillable="true"/>
						<xs:element name="Name" type="xs:string" minOccurs="0"/>
					</xs:sequence>
				</xs:complexType>
			</xs:element>
		</xs:sequence>
	</xs:complexType>
</xs:schema>

 

 

JSON XDTO интеграция валидация

См. также

Модуль для обмена "1С:Предприятие 8. УАТ. ПРОФ" с FortMonitor

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ 3. Отчеты по данным загруженным в регистры сведений. Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен.

11856 руб.

25.05.2021    11749    9    4    

8

Интеграция с сервисом vetmanager

WEB-интеграция Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Данная обработка разрабатывалась для загрузки документов из Ветменеджер в 1С: Бухгалтерия 3.

6000 руб.

02.02.2021    14449    34    43    

19

Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС

Обмен с ГосИС WEB-интеграция Платформа 1С v8.3 Управляемые формы 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия государственного учреждения 1С:Документооборот 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Платные (руб)

Обработка является альтернативой механизму, разработанному фирмой 1С и заполняющему реквизиты контрагента по ИНН или наименованию. Не требуется действующей подписки ИТС. Вызывается как внешняя дополнительная обработка, т.е. используется, непосредственно, из карточки контрагента. Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС (egrul.nalog.ru) для БП 2.0, БП 3.0, БГУ 1.0, БГУ 2.0, УТ 10.3, УТ 11.x, КА 1.1, КА 2.x, УПП 1.x, ERP 2.x, УНФ 1.5, УНФ 1.6, УНФ 3.0, ДО 2.1

2400 руб.

28.04.2016    85134    141    211    

296

Прайс-лист с фотографиями, выгрузкой в Excel с подсчетом суммы заказа, загрузкой заказа в Управление торговлей 11 (Россия) и Управление торговлей для Беларуси 3

Прайсы Загрузка и выгрузка в Excel Оптовая торговля WEB-интеграция Платформа 1С v8.3 Оперативный учет 1С:Управление торговлей 11 Россия Управленческий учет Платные (руб)

Прайс-лист для программы 1С: Управление торговлей 11 и Управление торговлей для Беларуси 3, позволяющий: 1) Формировать прайс-лист с фотографиями; 2) Сохранить прайс-лист в Excel с формулами, подсчитывающими количество и сумму заказа; 3) Передать сформированный прайс-лист по каналу ftp на сайт; 4) Сохранить прайс-лист в формате CSV; 5) Загрузить сделанный по прайс-листу заказ обратно в программу.

6000 руб.

04.09.2014    120786    44    105    

53

Sync1C: Синхронизация 1С и OpenCart

WEB-интеграция Оптовая торговля Розничная торговля Платформа 1С v8.3 Оперативный учет 1С:Управление торговлей 11 Управленческий учет Платные (руб)

Внешняя обработка для обмена данными с интернет-магазином OpenCart. Позволяет быстро наполнить магазин товарами, затем обновлять цены и добавлять новые товары. Далее можно средствами OpenCart настраивать и дополнять карточки товаров как надо для магазина, при этом связь товаров с 1С не теряется.

3840 руб.

30.03.2018    41845    78    133    

81

Merlion Commander Версия 1.3.9.2 - июль 2022 г. (Интеграция с 1С: УT, редакция 11.4, 1С:Розница 2.3,1С:ERP Управление предприятием 2, УТ 10.3, редакция веб-сервиса MERLION API 3.0 от 18.08.2021)

Оптовая торговля Розничная торговля WEB-интеграция Платформа 1С v8.3 1С:Управление торговлей 11 Россия Платные (руб)

Расширении конфигурации "Управление торговлей, редакция 11" для работы с веб-сервисом Мерлион с помощью Merlion API. Расширение и набор подключаемых дополнительных обработок позволяет без изменения конфигурации получить возможность работы с API крупнейшего российского дистрибьютора http://merlion.com. Логика работы максимально приближена к работе веб-сервиса b2b. Вы сможете создать и исправить заказ, зарезервировать товар прямо из 1С, посмотреть актуальные остатки и цены, импортировать штрихкода EAN13 товаров, загружать заказ c автоматическим созданием номенклатуры в 1С и корректности создания. Можно выбирать характеристики по товарным группам и загружать товар с выбранными характеристиками, загружать изображения товара. Не требуется установки дополнительного ПО для работы с веб-сервисом. Кроссплатформенное решение для ОС Windows и Linux. Весь код модулей открыт и доступен для просмотра и внесения изменений.

8280 руб.

02.05.2017    37362    40    45    

47
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. krbudaev 29.08.23 17:53 Сейчас в теме
Спасибо за статью. А не подскажите в каких ситуациях Вы видите использование schema для json удобнее/полезнее, чем ручной разбор json?
2. YA_418728146 234 29.08.23 18:18 Сейчас в теме
(1)
1. В моментах где надо контролировать наличие свойств, фасеты, длину строки - XDTO справится из коробки. Иначе придется писать много абстрактных методов, подтягивать регулярные выражения и в принципе усложнять валидацию.
2. Если не обрабатывать исключения при валидации через XDTO, при выбросе исключения вызывающая сторона получит статус 500 и в теле ответа подробное описание, почему валидация провалилась
3. Можно создавать свои объекты с набором свойств и правил и переиспользовать в других пакетах
3. Danila-Master 115 20.09.23 06:59 Сейчас в теме
А разве ПрочитатьДатуJSON(<Дата строкой>, ФорматДатыJSON.ISO) не решит проблему?
Оставьте свое сообщение