При разработке на платформе 1С:Предприятие 8 в режиме управляемого приложения программисты часто сталкиваются со специфическим поведением типов данных, предназначенных для работы формы. Одним из таких типов является ДанныеФормыСтруктура. Этот тип оптимизирован для передачи данных между клиентом и сервером, но имеет ряд жестких ограничений. Например, его нельзя обойти стандартным циклом Для Каждого, у него нет методов Вставить() или Удалить(), и его невозможно напрямую передать в системные функции, ожидающие обычную структуру.
Попытка выполнить прямое преобразование с помощью стандартного метода:
Поля = ДанныеФормыВЗначение(Объект, Тип("Структура"));
приводит к возникновению системной ошибки «Несоответствие типов (параметр номер '2')». Это происходит потому, что метод ДанныеФормыВЗначение предназначен исключительно для конвертации данных формы в прикладные объекты базы данных (такие как СправочникОбъект, ДокументОбъект и т. д.), но он совершенно не умеет работать с универсальной коллекцией Структура.
В этой статье мы подробно разберем четыре эффективных способа, которые помогут решить эту задачу в зависимости от контекста выполнения вашего кода.
Проанализируем наиболее простой и производительный сценарий. Если вы точно знаете, какие именно поля (свойства) входят в состав вашего объекта ДанныеФормыСтруктура, то нет необходимости прибегать к сложным динамическим алгоритмам. Достаточно использовать встроенную функцию ЗаполнитьЗначенияСвойств.
Разберем этот метод по шагам:
Структура и сразу инициализируем его ключами, имена которых совпадают с именами реквизитов в исходном объекте формы.ЗаполнитьЗначенияСвойств, которая быстро скопирует значения из источника в приемник по совпадающим именам свойств.Посмотрим на пример реализации данного подхода:
// Создаем структуру с заранее известным списком ключей
НоваяСтруктура = Новый Структура("Код, Наименование, Артикул, РозничнаяЦена");
// Быстро копируем значения из данных формы в созданную структуру
ЗаполнитьЗначенияСвойств(НоваяСтруктура, Объект);
Преимущества метода: максимальное быстродействие, отсутствие накладных расходов на чтение метаданных или сериализацию, работает как на клиенте, так и на сервере.
Выясним, как поступить, если состав реквизитов формы заранее неизвестен или может изменяться динамически. Если конвертируемый объект представляет собой реквизит текущей управляемой формы (например, основной реквизит Объект), мы можем задействовать контекстный метод формы ПолучитьРеквизиты(). Этот метод доступен только на сервере.
Рассмотрим подробнее алгоритм реализации:
ПолучитьРеквизиты() без параметров (чтобы получить реквизиты верхнего уровня) или с указанием пути к конкретному подчиненному элементу.РеквизитФормы.ЗаполнитьЗначенияСвойств для копирования данных.Пример программного кода для реализации этого способа:
&НаСервере
Функция ПреобразоватьДанныеФормыВСтруктуру(ДанныеФормы)
Результат = Новый Структура;
// Получаем список всех реквизитов текущей управляемой формы
МассивРеквизитов = ПолучитьРеквизиты();
// Перебираем полученные реквизиты и добавляем их имена в структуру
Для Каждого Реквизит Из МассивРеквизитов Цикл
// Исключаем служебные или неподходящие реквизиты при необходимости
Результат.Вставить(Реквизит.Имя, Неопределено);
КонецЦикла;
// Заполняем структуру реальными значениями из объекта ДанныеФормыСтруктура
ЗаполнитьЗначенияСвойств(Результат, ДанныеФормы);
Возврат Результат;
КонецФункции
Важное ограничение: этот метод жестко привязан к контексту управляемой формы и не может быть использован в общих модулях вне контекста формы.
Рассмотрим наиболее гибкий и по-настоящему универсальный обходной путь. Он основан на механизме XML-сериализации XDTO, встроенном в платформу 1С. Поскольку ДанныеФормыСтруктура поддерживает сериализацию, мы можем выгрузить объект в XML-строку, а затем прочитать структуру XML-документа, чтобы динамически вытащить имена всех свойств (тегов) и их значения.
Давайте разберем этот процесс по шагам:
ДанныеФормыСтруктура во временную строку XML при помощи встроенного механизма СериализаторXDTO.ЗаписатьXML.ЧтениеXML и начинаем разбор полученной строки.Структуру.Посмотрим на практический пример реализации такой функции:
Функция ПреобразоватьДанныеФормыВСтруктуруСериализацией(ДанныеФормы) Экспорт
Результат = Новый Структура;
// Записываем данные формы в XML
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ДанныеФормы, "FormObject", НазначениеТипаXML.Явное);
СтрокаXML = ЗаписьXML.Закрыть();
// Читаем полученную XML-строку для восстановления структуры
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаXML);
// Переходим к чтению содержимого
Пока ЧтениеXML.Прочитать() Цикл
// Нас интересуют только открывающие теги элементов внутри объекта
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя <> "FormObject" Тогда
ИмяСвойства = ЧтениеXML.Имя;
// Добавляем ключ в структуру
Результат.Вставить(ИмяСвойства, Неопределено);
КонецЕсли;
КонецЦикла;
// Наполняем структуру значениями
ЗаполнитьЗначенияСвойств(Результат, ДанныеФормы);
Возврат Результат;
КонецФункции
Преимущества метода: абсолютная независимость от контекста формы. Код можно разместить в любом общем модуле и вызывать его как на стороне сервера, так и на стороне тонкого клиента (при условии доступности объектов сериализации). Для разбора сериализованных данных также можно использовать готовую функцию преобразования XML в структуру.
В сценариях, когда под переменной Объект в форме скрывается полноценный прикладной объект (например, документ или справочник), правильным архитектурным решением будет сначала преобразовать данные формы в их реальный серверный тип, а затем получить структуру его реквизитов.
Разберем по шагам, как это сделать правильно на сервере:
ДанныеФормыВЗначение, передав вторым параметром точный тип метаданных прикладного объекта.Пример кода без использования БСП (чистая платформа):
&НаСервере
Функция ПолучитьСтруктуруРеквизитовОбъекта(ДанныеФормыОбъекта, ИмяТипаМетаданных)
// Пример значения ИмяТипаМетаданных: "СправочникОбъект.Номенклатура"
ПрикладнойОбъект = ДанныеФормыВЗначение(ДанныеФормыОбъекта, Тип(ИмяТипаМетаданных));
Результат = Новый Структура;
// Заполняем структуру стандартными и дополнительными реквизитами из метаданных
Для Каждого Реквизит Из ПрикладнойОбъект.Метаданные().Реквизиты Цикл
Результат.Вставить(Реквизит.Имя, ПрикладнойОбъект[Реквизит.Имя]);
КонецЦикла;
// Не забываем про стандартные реквизиты (например, Ссылка, Код, Наименование)
Для Каждого СтандартныйРеквизит Из ПрикладнойОбъект.Метаданные().СтандартныеРеквизиты Цикл
ИмяРеквизита = СтандартныйРеквизит.Имя;
Результат.Вставить(ИмяРеквизита, ПрикладнойОбъект[ИмяРеквизита]);
КонецЦикла;
Возврат Результат;
КонецФункции
При использовании Библиотеки Стандартных Подсистем (БСП) данная задача упрощается до одной строки. Вы можете использовать функции копирования из общего модуля ОбщегоНазначенияКлиентСервер или ОбщегоНазначения:
// Пример использования БСП для создания копии структуры свойств
НоваяСтруктура = ОбщегоНазначения.ТаблицаЗначенийВМассивСтруктур(ТаблицаИсточеник); // Для таблиц
// Или рекурсивное копирование свойств прикладного объекта:
НоваяСтруктура = ОбщегоНазначенияКлиент.СкопироватьРекурсивно(ПрикладнойОбъект);
Выбор оптимального метода преобразования типа ДанныеФормыСтруктура в обычную Структуру зависит от вашей конкретной задачи:
ЗаполнитьЗначенияСвойств). Это самый быстрый и безопасный вариант.ПолучитьРеквизиты().