Как передать заполненный массив или соответствие с сервера на клиент без потери данных?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработка в среде управляемого интерфейса 1С:Предприятие 8 часто преподносит сюрпризы, связанные с передачей данных между клиентом и сервером. Типичный сценарий: на сервере заполняется сложная структура, содержащая вложенные массивы и соответствия, которая записывается в реквизит формы с произвольным типом данных. Однако при возврате на клиент разработчик обнаруживает, что часть данных (особенно находящихся внутри соответствий) бесследно исчезла или затерлась.

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

Выясняем причину: почему данные пропадают из реквизита формы?

Для понимания сути проблемы проанализируем, как управляемая форма обменивается данными с сервером. Этот процесс называется «круглым путем» (round-trip). Когда выполняется серверный вызов, весь контекст формы сериализуется (преобразуется в специальный внутренний формат передачи данных), отправляется на сервер, там обрабатывается, сериализуется обратно и возвращается на клиент.

Если реквизит формы имеет тип Произвольный, платформа пытается упаковать и передать все находящиеся в нем объекты. И здесь мы сталкиваемся со следующими жесткими ограничениями:

  1. Ограничение типа Соответствие (Map): Объект типа Соответствие принципиально не может быть сохранен в реквизитах формы. Платформа 1С не умеет сериализовать динамические соответствия для контекста формы ни напрямую, ни во вложенном виде (например, внутри структуры, которая лежит в реквизите формы). При передаче на клиент такие узлы просто игнорируются или очищаются.
  2. Ограничение произвольного типа: Реквизиты с типом Произвольный оптимизируются платформой в целях экономии трафика. Если платформа «считает», что тип или его содержимое не поддерживаются на клиенте, данные будут молча отброшены во время обратной десериализации.
  3. Несоответствие типов данных формы: Для отображения коллекций на клиенте платформа использует специальные типы данных формы (например, ДанныеФормыКоллекция, ДанныеФормыСтруктура). Обычное Соответствие не имеет аналога в семействе «Данных формы», в отличие от массивов или структур.

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

Решение 1: Использование Временного хранилища (вариант автора темы)

Это классический и самый надежный способ передачи абсолютно любых объектов 1С, которые поддерживают сериализацию (включая сложные вложенные соответствия, таблицы значений и т.д.). Суть метода заключается в том, что мы не храним сами данные в реквизите формы. Вместо этого мы помещаем объект во временное хранилище сеанса на сервере, а в реквизите формы (обычная строка) сохраняем только уникальный адрес этого хранилища.

Разберем по шагам, как реализовать этот механизм.

Шаг 1. На сервере подготавливаем данные, помещаем их во временное хранилище и записываем адрес в строковый реквизит формы (например, АдресПараметровВоВременномХранилище):


&НаСервере
Процедура ПодготовитьИСохранитьДанныеНаСервере()
    
    // Создаем сложную структуру данных
    СтруктураПараметров = Новый Структура;
    СтруктураПараметров.Вставить("ОписаниеЗагрузки", "Импорт номенклатуры");
    
    ОшибкиСопоставления = Новый Соответствие;
    ОшибкиСопоставления.Вставить("Строка_15", "Не найден артикул");
    ОшибкиСопоставления.Вставить("Строка_42", "Неверная единица измерения");
    
    СтруктураПараметров.Вставить("ОшибкиСопоставления", ОшибкиСопоставления);
    СтруктураПараметров.Вставить("ОбщиеОшибки", Новый Массив);
    
    // Помещаем во временное хранилище, привязывая к УникальномуИдентификатору формы
    АдресДанных = ПоместитьВоВременноеХранилище(СтруктураПараметров, ЭтаФорма.УникальныйИдентификатор);
    
    // Записываем адрес в реквизит формы
    Объект.АдресПараметровВоВременномХранилище = АдресДанных;
    
КонецПроцедуры

Шаг 2. На клиенте или при повторном обращении к серверу мы можем легко извлечь эти данные по сохраненному адресу:


&НаСервере
ПроцедураОбработатьДанныеИзХранилища()
    
    // Проверяем наличие адреса
    Если ЗначениеЗаполнено(Объект.АдресПараметровВоВременномХранилище) Тогда
        
        // Получаем объект в первозданном виде со всеми соответствиями
        СтруктураПараметров = ПолучитьИзВременногоХранилища(Объект.АдресПараметровВоВременномХранилище);
        
        // Выполняем дальнейшую обработку
        Ошибки = СтруктураПараметров.ОшибкиСопоставления;
        // ...
        
    КонецЕсли;
    
КонецПроцедуры

Важное преимущество: Данные во временном хранилище, привязанные к уникальному идентификатору формы, автоматически удаляются платформой при закрытии формы. Это исключает утечки памяти сеанса.

Решение 2: Использование ФиксированногоСоответствия

Если вам необходимо передавать соответствие с сервера на клиент для чтения, и при этом вы хотите хранить его именно внутри реквизита формы, стандартное Соответствие использовать нельзя. Однако платформа 1С предоставляет специализированный тип — ФиксированноеСоответствие.

Этот тип данных официально поддерживается платформой для хранения в реквизитах управляемой формы (как напрямую, так и внутри вложенных структур). Единственное его ограничение — он является неизменяемым («только для чтения») после создания.

Проанализируем пример конвертации динамического соответствия в фиксированное перед записью в реквизит формы на сервере:


&НаСервере
Процедура ЗаполнитьРеквизитФиксированнымСоответствием()
    
    // Создаем обычную структуру параметров загрузки
    СтруктураПараметров = Новый Структура;
    
    // Создаем и заполняем стандартное динамическое соответствие
    ДинамическоеСоответствие = Новый Соответствие;
    ДинамическоеСоответствие.Вставить("Ключ1", "Значение1");
    ДинамическоеСоответствие.Вставить("Ключ2", "Значение2");
    
    // Преобразуем в ФиксированноеСоответствие при записи в структуру
    СтруктураПараметров.Вставить("ОшибкиСопоставления", Новый ФиксированноеСоответствие(ДинамическоеСоответствие));
    
    // Теперь эту структуру можно безопасно записать в произвольный реквизит формы
    ЭтаФорма.СтруктураПараметровРеквизит = СтруктураПараметров;
    
КонецПроцедуры

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

Решение 3: Замена Соответствий на Структуры

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

Очень часто разработчики используют Соответствие по привычке, хотя ключами в нем выступают обычные строки. Если это ваш случай, настоятельно рекомендуется полностью заменить тип Соответствие на тип Структура.

Вложенные структуры и массивы внутри реквизитов формы передаются платформой между клиентом и сервером гораздо стабильнее и не подвержены риску «тихого» исчезновения данных при десериализации. Если же вам обязательно нужно использовать в качестве ключа ссылку или число, вы можете преобразовать их в строковое представление (например, использовать уникальный идентификатор ссылки или строковое представление числа с префиксом) и записывать данные в структуру.

Решение 4: Сериализация в JSON (универсальный бесконтекстный метод)

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

Сериализация на уровне платформы работает чрезвычайно быстро. Рассмотрим, как это реализовать.

Код упаковки структуры на сервере:


&НаСервере
Функция ЗапаковатьДанныеВJSON(СтруктураДанных)
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    
    // Записываем структуру в строку JSON
    ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
    
    Возврат ЗаписьJSON.Закрыть();
    
КонецФункции

Код распаковки структуры на клиенте:


&НаКлиенте
Функция РаспаковатьДанныеИзJSON(СтрокаJSON)
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
    
    // Читаем структуру обратно в объект 1С
    Возврат ПрочитатьJSON(ЧтениеJSON);
    
КонецФункции

Строка JSON гарантированно и без искажений передается в любом реквизите формы с типом Строка неограниченной длины. На клиенте вы в любой момент можете быстро «распаковать» ее в обычную структуру 1С и работать с ней.

Решение 5: Отказ от хранения в контексте формы (передача через параметры функций)

Часто программисты используют реквизиты формы как «склад» для временных данных алгоритма. Попробуем проанализировать ситуацию: действительно ли эти данные должны постоянно жить в форме и передаваться туда-обратно при каждом серверном вызове?

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

Посмотрим на пример правильной организации вызовов:


&НаКлиенте
Процедура ВыполнитьОбработкуДанных()
    
    // Получаем структуру данных напрямую из функции, минуя реквизиты формы
    ПараметрыЗагрузки = ПолучитьПараметрыНаСервере();
    
    // Модифицируем или анализируем данные на клиенте
    Если ПараметрыЗагрузки.ОбщиеОшибки.Количество() > 0 Тогда
        Предупреждение("Обнаружены ошибки при подготовке данных!");
    КонецЕсли;
    
    // Передаем измененные данные обратно на сервер без привязки к контексту формы
    ЗаписатьДанныеНаСервере(ПараметрыЗагрузки);
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьПараметрыНаСервере()
    
    Параметры = Новый Структура;
    Параметры.Вставить("ОбщиеОшибки", Новый Массив);
    // Заполнение параметров...
    
    Возврат Параметры;
    
КонецФункции

&НаСервереБезКонтекста
Процедура ЗаписатьДанныеНаСервере(Знач ПараметрыЗагрузки)
    
    // Обработка и сохранение данных на сервере
    // Использование ключевого слова "Знач" предотвращает лишнее копирование данных на клиент
    
КонецПроцедуры

Использование директивы &НаСервереБезКонтекста существенно ускоряет работу приложения, так как при вызове процедур не передаются данные самой формы, а передаются только те параметры, которые мы явно указали в вызове функции. Для оценки производительности различных типов обращений можно воспользоваться исследованием эффективности контекстных и неконтекстных вызовов.

Резюме и рекомендации

Подводя итоги, дадим несколько практических советов для выбора оптимального решения:

← На главную