Валидация 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 интеграция валидация

См. также

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

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

22656 руб.

25.05.2021    13442    39    8    

15

Управление взаимоотношениями с клиентами (CRM) WEB-интеграция Анализ продаж Системный администратор Программист Пользователь Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Управленческий учет Платные (руб)

Подсистема интеграции Amo CRM с 1С: технические требования, порядок работы, возможности, доработки и обновления. Бесплатный период техподдержки - 1 месяц.

60000 руб.

07.05.2019    31997    62    41    

23

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

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    16842    15    19    

15

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

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

12000 руб.

02.02.2021    17084    46    49    

26

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

Интеграция 1С и Битрикс 24. Разработка имеет двухстороннюю синхронизацию 1С и Bitrix24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    19155    10    17    

16
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. krbudaev 29.08.23 17:53 Сейчас в теме
Спасибо за статью. А не подскажите в каких ситуациях Вы видите использование schema для json удобнее/полезнее, чем ручной разбор json?
2. YA_418728146 555 29.08.23 18:18 Сейчас в теме
(1)
1. В моментах где надо контролировать наличие свойств, фасеты, длину строки - XDTO справится из коробки. Иначе придется писать много абстрактных методов, подтягивать регулярные выражения и в принципе усложнять валидацию.
2. Если не обрабатывать исключения при валидации через XDTO, при выбросе исключения вызывающая сторона получит статус 500 и в теле ответа подробное описание, почему валидация провалилась
3. Можно создавать свои объекты с набором свойств и правил и переиспользовать в других пакетах
3. Danila-Master 118 20.09.23 06:59 Сейчас в теме
А разве ПрочитатьДатуJSON(<Дата строкой>, ФорматДатыJSON.ISO) не решит проблему?
4. aleksey_vk 7 05.10.23 13:53 Сейчас в теме
Получилось передать поле-массив вида {"nums": [1, 2, 3]}:
<xs:element name="nums" type="xs:int" maxOccurs="999"/>

При чтении JSON для поля получаем СписокXDTO с нужными значениями.
версия лпатформы 8.3.22.1750
Windsor77; anton.fly7; LOSTuK; +3 Ответить
5. Onwardv 65 18.10.23 13:00 Сейчас в теме
Так-то совсем не рекомендую использовать XDTO. Намучаешься с ним.
Ручной разбор Json гораздо практичнее.
6. potoyalo 23.11.23 07:55 Сейчас в теме
Подскажите, как описать xdto-пакет, чтобы можно было восстановить пустой массив объектов:
{"Objects": []}


Делаю примерно так:
<xs:schema ...>
	<xs:complexType name="QueryResult.Objects">
		<xs:choice>
			<xs:element name="rows" minOccurs="0" maxOccurs="unbounded">
				<xs:complexType>
					<xs:complexContent>
						<xs:extension base="tns:Object"/>
					</xs:complexContent>
				</xs:complexType>
			</xs:element>
		</xs:choice>
	</xs:complexType>
	<xs:complexType name="Object">
		<xs:all>
			<xs:element name="id" type="xs:int"/>
			<xs:element name="name" type="xs:string"/>
		</xs:all>
	</xs:complexType>
</xs:schema>
Показать


Массив с данными восстанавливается корректно, на пустом массиве получаю ошибку:

Ошибка преобразования данных XDTO:
КонецСвойства: Форма: Элемент Тип: {...}QueryResult.Object
7. kasper076 109 15.05.24 11:51 Сейчас в теме
(6) Вариант схемы:
<xs:schema ...>
	<xs:element name="Array">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="Objects" type="xs:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>
Показать


Строка JSON с пустым массивом:
"{"#value": []}"

Из-за ограничений возможностей ФабрикиXDTO по чтению JSON вместо Objects необходимо указывать #value.
user1572509; +1 Ответить
Оставьте свое сообщение