При разработке в 1С:Предприятие на управляемых формах мы часто сталкиваемся с необходимостью обработать переданные в форму параметры. Если мы попытаемся обратиться к ЭтаФорма.Параметры в обработчике ПриСозданииНаСервере, то обнаружим, что этот объект имеет специфический тип ДанныеФормыСтруктура. Зачастую нам необходимо преобразовать этот объект в обычную платформенную Структуру для дальнейшей передачи в процедуры, комфортного перебора в цикле или модификации.
Рассмотрим подробнее, почему стандартные средства платформы выдают ошибку на этом шаге, и разберем по шагам рабочие способы решения этой задачи.
Многие разработчики по логике вещей пытаются использовать встроенную функцию платформы и пишут конструкцию вида:
СтруктураПараметров = ДанныеФормыВЗначение(ЭтаФорма.Параметры, Тип("Структура"));
Однако при выполнении этой команды программа гарантированно "вылетает" с ошибкой несоответствия типов — для отладки подобных ошибок в режиме предприятия пригодится инструмент написания и отладки кода 1С. Выясним причину. Метод ДанныеФормыВЗначение предназначен платформой для конвертации исключительно в прикладные объекты (например, СправочникОбъект, ДокументОбъект, НаборЗаписей). Тип Структура относится к базовым универсальным коллекциям, и платформа просто не умеет распаковывать данные управляемой формы в этот тип напрямую. Более того, коллекция ДанныеФормыСтруктура не поддерживает стандартный перебор через Для Каждого Параметр Из Параметры Цикл, что делает невозможным простое копирование "в лоб".
Начиная с версий платформы 8.3.7, самым лаконичным и быстрым способом является сериализация через JSON. Этот способ избавляет нас от написания громоздких циклов парсинга узлов.
Проанализируем алгоритм:
ПрочитатьJSON(), который автоматически формирует из нее привычную нам Структуру (или Соответствие).Посмотрим на пример реализации данного подхода:
&НаСервере
Функция ПреобразоватьПараметрыВструктуруJSON(ПараметрыФормы)
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
// Сериализуем данные формы в JSON
СериализаторXDTO.ЗаписатьJSON(Запись, ПараметрыФормы);
СтрокаJSON = Запись.Закрыть();
// Читаем обратно
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(СтрокаJSON);
// Метод ПрочитатьJSON автоматически создаст структуру
Результат = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
// Удаляем служебное поле типа, если оно появилось
Если Результат.Свойство("#type") Тогда
Результат.Удалить("#type");
КонецЕсли;
Возврат Результат;
КонецФункции
Важно: Если в параметрах формы содержатся несериализуемые типы (например, ссылки на элементы управления формой, открытые формы или специфичные COM-объекты), сериализация выдаст ошибку. В таком случае в метод ЗаписатьJSON можно передать дополнительную функцию преобразования, которая будет игнорировать проблемные типы, заменяя их на Неопределено.
Если по какой-то причине JSON-сериализация не подходит (например, используются старые версии платформы), мы можем сериализовать объект в XML или бинарный XML (FastInfoset), а затем программно разобрать узлы, чтобы получить список имен параметров.
Разберем по шагам готовое решение:
&НаКлиентеНаСервереБезКонтекста
Функция ПолучитьСтруктуруДанныхФормы(ДанныеФормы)
Массив = Новый Массив;
Массив.Добавить(ДанныеФормы);
Запись = Новый ЗаписьFastInfoset;
Запись.УстановитьДвоичныеДанные();
СериализаторXDTO.ЗаписатьXML(Запись, Массив);
Чтение = Новый ЧтениеFastInfoset;
Чтение.УстановитьДвоичныеДанные(Запись.Закрыть());
Результат = Новый Структура;
Пока Чтение.Прочитать() Цикл
Если Чтение.Имя = "field" И Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяПоля = Чтение.ЗначениеАтрибута("nameRu");
Если ИмяПоля = Неопределено Тогда
ИмяПоля = Чтение.ЗначениеАтрибута("name");
КонецЕсли;
// Исключаем системные поля
Если ИмяПоля <> Неопределено
И ИмяПоля <> "СтандартнаяКартинкаСтроки"
И ИмяПоля <> "СтрокаДинамическогоСписка.ГруппировкаСтроки" Тогда
// Вставляем ключ и сразу получаем значение из исходных данных
Результат.Вставить(ИмяПоля, ДанныеФормы[ИмяПоля]);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
В этом примере мы читаем структуру свойств, а затем сразу заполняем новую структуру реальными значениями, обращаясь к свойствам через квадратные скобки: ДанныеФормы[ИмяПоля].
Если наша коллекция ДанныеФормыСтруктура — это не просто произвольные параметры, а основной реквизит формы (например, структура объекта документа или справочника), мы можем получить список полей из метаданных этого объекта.
Посмотрим на пример такого перебора:
МетаданныеТипа = Метаданные.НайтиПоТипу(ТипЗнч(дфс.Ссылка));
резСтруктура = Новый Структура;
резСтруктура.Вставить("Ссылка", дфс.Ссылка);
Для каждого Реквизит из МетаданныеТипа.Реквизиты Цикл
резСтруктура.Вставить(Реквизит.Имя, дфс[Реквизит.Имя]);
КонецЦикла;
Этот метод хорош тем, что не использует сериализацию и работает очень быстро, однако он применим только тогда, когда состав полей структуры строго соответствует метаданным прикладного объекта — для быстрого исследования структуры метаданных и типов объектов пригодится набор инструментов разработчика 1С для управляемых форм.
Чтобы вообще не сталкиваться с проблемой десериализации параметров при открытии формы, мы можем применить правильный архитектурный подход. В современных конфигурациях на базе БСП применяется хитрость: нужные параметры собираются в обычную структуру еще до открытия формы и передаются как одно из свойств-параметров.
Вместо того чтобы передавать ключи в корень параметров, мы создаем вложенную структуру:
// На стороне вызывающего кода
ПараметрыОткрытия = Новый Структура;
ДопПараметры = Новый Структура("Сотрудник, Подразделение", ТекущийСотрудник, ТекущееПодразделение);
ПараметрыОткрытия.Вставить("ДопПараметры", ДопПараметры);
ОткрытьФорму("Обработка.МояОбработка.Форма", ПараметрыОткрытия);
Тогда внутри формы в ПриСозданииНаСервере мы обращаемся к Параметры.ДопПараметры. Тип этого вложенного свойства останется Структура, и его не придется конвертировать — мы сможем сразу обойти его с помощью цикла Для Каждого.
Альтернативный вариант — использовать подписку на событие или обработчик в модуле менеджера ОбработкаПолученияФормы. Там мы можем скопировать оригинальную структуру параметров:
Процедура ОбработкаПолученияФормы(ВидФормы, Параметры, ВыбраннаяФорма, ДополнительнаяИнформация, СтандартнаяОбработка)
Если ВидФормы = "ФормаОбъекта" Тогда
// На этом этапе Параметры — это еще обычная структура
ПараметрыСтруктура = ОбщегоНазначения.СкопироватьРекурсивно(Параметры);
Параметры.Вставить("ПараметрыСтруктура", ПараметрыСтруктура);
КонецЕсли;
КонецПроцедуры
Используя методы сериализации (JSON или XML), помните об одной важной особенности: дублирование имен. Если в ДанныеФормыСтруктура окажутся свойства с одинаковыми именами (например, имя реквизита документа совпадает с именем табличной части, что допускается платформой в некоторых контекстах формы), то при вставке в Структуру они перезапишут друг друга. Коллекция Структура в 1С требует строгой уникальности ключей, поэтому в итоговую структуру попадет только последнее считанное значение.
Применяйте описанные методы исходя из контекста вашей задачи: для простых произвольных параметров используйте JSON-сериализацию, для объектов предметной области — перебор через метаданные, а при проектировании нового функционала старайтесь закладывать вложенную структуру до передачи параметров в форму.