При создании 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.
Подписывайтесь на мой канал (наверху), будет много интересного бесплатного контента :)