Чтение вложенных свойств Структур Структуры, Соответствий, свойства через точку, разбор 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

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3400    Abysswalker    8    

45

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

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

14.05.2025    6325    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    48667    dimanich70    83    

169

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

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

1 стартмани

18.03.2024    7294    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

12.02.2024    60849    atdonya    31    

69

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

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

30.11.2023    9085    ke.92@mail.ru    17    

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