Чтение вложенных свойств Структур Структуры, Соответствий, свойства через точку, разбор JSON

21.12.20

Разработка - Универсальные функции

JSON: {user.device.type} - как получить значение {type}? А если вложенность значительно глубже? Как проверить, что оно заполнено или удалить его - всё это в публикации с открытым кодом и даже без рекурсии. Бонусом разбор дерева значений - ДанныеФормыЭлементДерева, СтрокаДереваЗначений.

При создании Dashboard, B2B-порталов или прочих веб-приложений на базе EDIbot нам часто приходится работать с большими вложенными JSON, читать нужные нам свойства, на какой глубине вложенности они бы не находились. Поэтому мы создали удобную для себя функцию, которую сейчас вместе с Вами препарируем. 

 

Итак, начнем реализацию с создания имени нашей функции - "ПолучитьЗначениеКоллекции", по аналогии с функциями БСП ("СкопироватьУниверсальнуюКоллекцию"). Параметрами у нас будут: Коллекция, ИмяСвойства, ЗначениеПоУмолчанию, МягкийРежим. 

Закладываем следующую логику работы функции:

1. Проверяем Коллекцию на возможность обработки. 

2. Получаем Коллекцию {device}, которая содержит наше свойство {type}.

3. Получаем и возвращаем свойство {type} коллекции {device}.

 

Рассмотрим более детально. 

Для удобства работы с функцией "ПолучитьЗначениеКоллекции" параметром "Коллекция" может быть не только Структура или Соответствие, но и значение простого типа - Дата, Число, Булево, Строка.  Вы скажете, зачем? Но когда мы разбираем сложные и большие структуры, мы не всегда знаем, какого типа значение, которое мы хотим разобрать, и удобно в качестве ответа получить само по себе переданное значение (коллекцию). Отразим это в коде функции "ПолучитьЗначениеКоллекции":

Если (ТипЗнч(Коллекция)=Тип("Число") ИЛИ ТипЗнч(Коллекция)=Тип("Строка") ИЛИ ТипЗнч(Коллекция)=Тип("Булево") ИЛИ ТипЗнч(Коллекция)=Тип("Дата")) Тогда
		
	Если МягкийРежим И ЗначениеЗаполнено(Коллекция) Тогда
		Возврат Коллекция;	
	Иначе
		Возврат ЗначениеПоУмолчанию;
	КонецЕсли;	
		
КонецЕсли;

 

Далее, нам нужно получить последнюю коллекцию  {user.device.type}, т.е. коллекцию {device}, из которой потом уже получим нужное нам свойство {type}. Поэтому создадим функцию ПолучитьКоллекциюПоСвойству(Коллекция, Знач ИмяСвойства, ЗначениеПоУмолчанию=Неопределено) и вызовем ее. 

Результат = ПолучитьКоллекциюПоСвойству(Коллекция, ИмяСвойства, ЗначениеПоУмолчанию);

Получив в качестве Результата последнюю коллекцию {device}, мы можем получить ее свойство {type}. Для этого мы создадим функцию "ПолучитьЗначениеКоллекцииБезВложенности", вызов Возврат ПолучитьЗначениеКоллекцииБезВложенности(Результат.Коллекция, Результат.ИмяСвойства, ЗначениеПоУмолчанию) вернет значение свойства {type} коллекции {device}. 

 

Таким образом, наша с Вами функция выглядит следующим образом:

Функция ПолучитьЗначениеКоллекции(Знач Коллекция, Знач ИмяСвойства, ЗначениеПоУмолчанию=Неопределено, МягкийРежим=Истина) экспорт
	
	Если (ТипЗнч(Коллекция)=Тип("Число") ИЛИ ТипЗнч(Коллекция)=Тип("Строка") ИЛИ ТипЗнч(Коллекция)=Тип("Булево") ИЛИ ТипЗнч(Коллекция)=Тип("Дата")) Тогда
		
		Если МягкийРежим И ЗначениеЗаполнено(Коллекция) Тогда
			Возврат Коллекция;	
		Иначе
			Возврат ЗначениеПоУмолчанию;
		КонецЕсли;	
		
	КонецЕсли;
	
	Результат = ПолучитьКоллекциюПоСвойству(Коллекция, ИмяСвойства, ЗначениеПоУмолчанию);
	
	Возврат ПолучитьЗначениеКоллекцииБезВложенности(Результат.Коллекция, Результат.ИмяСвойства, ЗначениеПоУмолчанию);
		
КонецФункции

 

Теперь рассмотрим построение функции "ПолучитьКоллекциюПоСвойству". 

Для начала получим массив переданных нам свойств:

МассивСвойств = СтрРазделить(ИмяСвойства, "."); 

А далее, по очереди в цикле, каждое свойство обработаем, записывая в результат текущее значение коллекции и передавая его на следующем этапе цикла в качестве значения, но кроме последнего. 

Для Ном=0 По МассивСвойств.Количество()-2 Цикл                
       Результат.Вставить("Коллекция",    ПолучитьЗначениеКоллекцииБезВложенности(Результат.Коллекция, СокрЛП(МассивСвойств[Ном]), ЗначениеПоУмолчанию));
КонецЦикла;    

Иными словами, мы с Вами реализовали рекурсивное чтение, но без рекурсии, что конкретно в данном случае нам позволило чуть с меньшим количеством кода и без нагрузки на стек вызовов реализовать поставленную задачу. 

Итак, наша функция целиком "ПолучитьКоллекциюПоСвойству":

Функция ПолучитьКоллекциюПоСвойству(Коллекция, Знач ИмяСвойства, ЗначениеПоУмолчанию=Неопределено) экспорт
	
	Результат = Новый Структура();
	Результат.Вставить("Коллекция",		Коллекция);
	Результат.Вставить("ИмяСвойства",	ИмяСвойства);
	
	Если НЕ ТипЗнч(ИмяСвойства)=Тип("Строка") Тогда
		Возврат Результат;
	КонецЕсли;
			
	МассивСвойств = СтрРазделить(ИмяСвойства, ".");	
	
	Если МассивСвойств.Количество()=1 Тогда		
		Возврат Результат;
	КонецЕсли;
	
	
	Для Ном=0 По МассивСвойств.Количество()-2 Цикл				
		Результат.Вставить("Коллекция",	ПолучитьЗначениеКоллекцииБезВложенности(Результат.Коллекция, СокрЛП(МассивСвойств[Ном]), ЗначениеПоУмолчанию));
	КонецЦикла;		
	
	Результат.Вставить("ИмяСвойства",	СокрЛП(МассивСвойств[Ном])); 
		
	Возврат Результат;

КонецФункции	

 

Осталось реализовать последнюю функцию "ПолучитьЗначениеКоллекцииБезВложенности", привожу ее исходный код:

Функция ПолучитьЗначениеКоллекцииБезВложенности(Знач Коллекция, ИмяСвойства, ЗначениеПоУмолчанию=Неопределено) экспорт
	
	#Если Сервер Тогда
	Если ТипЗнч(Коллекция)=Тип("СтрокаДереваЗначений") Тогда
		
		ДеревоЗначений = Коллекция.Владелец();
		
		Если НЕ ДеревоЗначений.Колонки.Найти(ИмяСвойства)=Неопределено Тогда
			 Возврат Коллекция[ИмяСвойства];
		КонецЕсли; 
	КонецЕсли;
	#КонецЕсли
	
	Если (ТипЗнч(Коллекция)=Тип("Структура") ИЛИ ТипЗнч(Коллекция)=Тип("ФиксированнаяСтруктура")) И НЕ ПустаяСтрока(ИмяСвойства) Тогда
		
		Попытка
			Если Коллекция.Свойство(ИмяСвойства)  И (НЕ ЗначениеНеЗаполнено(Коллекция[ИмяСвойства]) ИЛИ ЗначениеПоУмолчанию=Неопределено)  Тогда			
				Возврат Коллекция[ИмяСвойства];
			КонецЕсли;
		Исключение
			Возврат  ЗначениеПоУмолчанию;
		КонецПопытки;
		
	#Если Клиент Тогда		
	ИначеЕсли ТипЗнч(Коллекция)=Тип("ВнешнийОбъект") Тогда
		
		Попытка
			Возврат Коллекция[ИмяСвойства];
		Исключение
			Возврат ЗначениеПоУмолчанию; 
		КонецПопытки;	
				
	ИначеЕсли ТипЗнч(Коллекция)=Тип("ДанныеФормыЭлементДерева") И НЕ ПустаяСтрока(ИмяСвойства) Тогда
		
		Если Коллекция.Свойство(ИмяСвойства)  И (НЕ ЗначениеНеЗаполнено(Коллекция[ИмяСвойства]) ИЛИ ЗначениеПоУмолчанию=Неопределено)  Тогда			
			Возврат Коллекция[ИмяСвойства];
		КонецЕсли;
	#КонецЕсли
	ИначеЕсли ТипЗнч(Коллекция)=Тип("Соответствие") И (НЕ ЗначениеНеЗаполнено(Коллекция.Получить(ИмяСвойства)) ИЛИ ЗначениеПоУмолчанию=Неопределено)   Тогда
		Возврат Коллекция.Получить(ИмяСвойства);
	КонецЕсли;
	
	Возврат ЗначениеПоУмолчанию;
КонецФункции

 

И как обещал, еще две небольшие функции проверки заполненности значения коллекции:

Функция ЗначениеКоллекцииЗаполнено(Знач Коллекция, Знач ИмяСвойства) экспорт
		
	Результат = ПолучитьКоллекциюПоСвойству(Коллекция, ИмяСвойства);

	Возврат НЕ ЗначениеНеЗаполнено(ПолучитьЗначениеКоллекцииБезВложенности(Результат.Коллекция, Результат.ИмяСвойства));
		
КонецФункции

 

И функция удаления свойства коллекции:

Функция УдалитьСвойстваКоллекции(Коллекция, ИмяСвойства) экспорт
	
	Если ТипЗнч(Коллекция)=Тип("Структура") Тогда		
		НоваяКоллекция = Новый Структура();
	ИначеЕсли ТипЗнч(Коллекция)=Тип("Соответствие") Тогда	
		НоваяКоллекция = Новый Соответствие();	
	Иначе
		Возврат Коллекция;
	КонецЕсли;
	
	Результат = ПолучитьКоллекциюПоСвойству(Коллекция, ИмяСвойства);
	Если Результат.Коллекция=Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если ПустаяСтрока(Результат.ИмяСвойства) Тогда
		Возврат Результат.Коллекция;
	КонецЕсли;	
	
	Результат.Коллекция.Удалить(Результат.ИмяСвойства);
		
	Возврат Результат.Коллекция;
	
КонецФункции	

 

UPD. Выкладываю код функции ЗначениеНеЗаполнено

// Определяет заполнено ли переданное значение
//
// Параметры: 
//  Значение - значение, заполенение которого надо проверить
//
// Возвращаемое значение:
//  Истина - значение не заполнено, ложь - иначе.
//
Функция ЗначениеНеЗаполнено(Значение, БулевоЛожьЗаполнено=Истина) Экспорт
	
	Результат = Ложь;
	ТипЗначения = ТипЗнч(Значение);

	// Сначала примитивные типы
	Если Значение = Неопределено Тогда
		
		Результат = Истина;
		
	ИначеЕсли Значение = NULL Тогда
		
		Результат = Истина;
		
	ИначеЕсли ТипЗначения = Тип("Строка") Тогда
		
		Если СокрЛП(Значение) = "" Тогда
			Результат = Истина;
		КонецЕсли; 
		
	ИначеЕсли ТипЗначения = Тип("Число") Тогда
		
		Если Значение = 0 Тогда
			Результат = Истина;
		КонецЕсли; 
		
	ИначеЕсли ТипЗначения = Тип("Дата") Тогда
		
		Если Значение = Дата('00010101') Тогда
			Результат = Истина;
		КонецЕсли; 
		
	ИначеЕсли ТипЗначения = Тип("Булево") Тогда
		
		Если Значение=Истина Тогда
			Возврат Ложь;
		Иначе	
			Результат = НЕ БулевоЛожьЗаполнено; // Булево будем считать не пустым
		КонецЕсли;
		
	ИначеЕсли (ТипЗначения = Тип("Соответствие") ИЛИ ТипЗначения = Тип("Структура") ИЛИ ТипЗначения = Тип("Массив")) И Значение.Количество()=0 Тогда	
		
		Результат = Истина; 
	// Для остальных будем считать значение пустым, если оно равно
	// дефолтному значению своего типа
	Иначе
		
		Попытка
			
			Если Значение = Новый(ТипЗначения) Тогда
				Результат = Истина;
			КонецЕсли; 
			
		Исключение
		КонецПопытки;
		
	КонецЕсли; 

	Возврат Результат;
КонецФункции // ЗначениеНеЗаполнено()

 

UPD. Обновлена работа с Фиксированной структурой и ВнешнимОбъектом

 

Надеюсь, моя публикация была Вам полезна и сэкономит Ваше время, ссылка на все публикации SizovE.

Подписывайтесь на мой канал (наверху), будет много интересного бесплатного контента :)

SizovE

См. также

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    20113    dimanich70    81    

144

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    4090    3    John_d    11    

57

Универсальные функции Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    18039    atdonya    24    

56

Универсальные функции Программист Платформа 1С v8.3 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    5501    ke.92@mail.ru    16    

65

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

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

28.08.2023    14728    YA_418728146    7    

166

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

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    3578    56    progmaster    8    

4

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    18476    171    sapervodichka    112    

135
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Cmapnep 19 18.11.20 12:06 Сейчас в теме
В функции "ЗначениеКоллекцииЗаполнено" есть такой код: "Возврат НЕ ЗначениеНеЗаполнено(...)" - что за функция такая значениеНЕзаполнено?
2. SizovE 269 18.11.20 12:29 Сейчас в теме
(1) Когда-то давным давно до появления ЗначениеЗаполнено в типовых 1С использовали функцию ЗначениеНеЗаполнено. В данном случае, я тоже использовал именно ее из-за того, что ЗначениеЗаполнено не проверяет все типы, которые мне нужны. Например, заполнено ли поле типа Новый Цвет и т.п.. Добавлю сейчас в статью код этой функции.
Оставьте свое сообщение