Разработка и Конфигурация · 12 мин
1С: как передать выборку с сервера на клиент
Как передать выборку запроса с сервера на клиент в 1С: сериализация в массив структур, ограничения ТаблицыЗначений, примеры кода и типичные ошибки.
В управляемом приложении 1С запросы выполняются только на сервере, а отображение данных происходит на клиенте. Возникает типичная задача: выполнить запрос в серверной процедуре и передать результат в клиентский контекст — для заполнения таблицы на форме, вывода сообщений, перебора строк в цикле или дальнейшей обработки без повторных серверных вызовов.
Главное ограничение: между сервером и клиентом нельзя напрямую передавать объекты типа «ВыборкаИзРезультатаЗапроса», «РезультатЗапроса» и «ТаблицаЗначений» в произвольном серверном вызове — эти типы не сериализуются при передаче. Поэтому данные нужно упаковать в сериализуемые структуры: массив структур, строку JSON или реквизит формы.
Ниже разобраны рабочие подходы, их ограничения, готовые примеры кода и типичные ошибки, из-за которых возникают сообщения «Ошибка передачи данных между клиентом и сервером» или «Тип не может быть сериализован».
Почему выборку нельзя передать напрямую
Объект «ВыборкаИзРезультатаЗапроса» — это курсор, привязанный к результату запроса в памяти сервера. У него нет сериализуемого представления, поэтому попытка вернуть его из серверной процедуры приведёт к ошибке. То же относится к «РезультатЗапроса» и к объекту «Запрос» в целом.
Тип «ТаблицаЗначений» сериализуется, но только в ограниченных сценариях: его можно вернуть из серверной функции, вызванной с клиента, однако это работает не во всех режимах и создаёт заметный трафик. Надёжная и универсальная практика — преобразовать данные в массив структур, где значения полей имеют простые сериализуемые типы (строка, число, дата, булево, ссылка).
Способ 1. Массив структур — универсальное решение
Самый распространённый подход: в серверной функции выполнить запрос, обойти выборку и каждую строку упаковать в структуру. Массив структур полностью сериализуется и свободно передаётся на клиент.
&НаСервере
Функция ПолучитьДанныеНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
Результат = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Строка = Новый Структура("Ссылка, Наименование, Артикул");
ЗаполнитьЗначенияСвойств(Строка, Выборка);
Результат.Добавить(Строка);
КонецЦикла;
Возврат Результат;
КонецФункции
&НаКлиенте
Процедура Заполнить(Команда)
Данные = ПолучитьДанныеНаСервере();
Для Каждого Строка Из Данные Цикл
Сообщить(Строка.Наименование + " (" + Строка.Артикул + ")");
КонецЦикла;
КонецПроцедуры
Обратите внимание на приём ЗаполнитьЗначенияСвойств(Строка, Выборка): он копирует из выборки только те поля, которые объявлены в структуре. Это избавляет от ручного присваивания каждого поля и снижает риск опечаток. Имена полей в структуре должны совпадать с псевдонимами колонок запроса.
Ограничение метода — объём данных. Каждая структура несёт накладные расходы на хранение имён ключей, поэтому для выборок в десятки тысяч строк трафик и время сериализации становятся заметными. В таких случаях либо сокращайте набор полей, либо переходите к другим способам.
- 1Создайте серверную функцию с директивой &НаСервере
- 2Выполните запрос и получите выборку методом Выбрать()
- 3В цикле Пока Выборка.Следующий() заполняйте структуры и добавляйте их в массив
- 4Верните массив из функции
- 5На клиенте обойдите массив циклом Для Каждого
- 6Проверьте на тестовых данных: количество строк и значения полей совпадают с запросом в консоли
Способ 2. Реквизит формы «ТаблицаЗначений»
Если цель — показать данные пользователю в таблице на форме, самый правильный путь — не передавать ничего вручную. Добавьте реквизит формы типа «ТаблицаЗначений» с нужными колонками, заполните его на сервере, и платформа сама передаст данные на клиент при обновлении формы. Это штатный механизм, оптимизированный платформой.
&НаСервере
Процедура ЗаполнитьТаблицуНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ";
ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
ТаблицаНаФорме.Загрузить(ТаблицаРезультат);
КонецПроцедуры
Здесь ТаблицаНаФорме — реквизит формы. Метод Загрузить() требует, чтобы состав колонок таблицы значений совпадал с колонками реквизита, иначе данные по несовпадающим колонкам будут потеряны или возникнет ошибка. Если состав колонок динамический, используйте ИзменитьРеквизиты() для программного добавления колонок перед загрузкой.
Для иерархических данных аналогично работает реквизит типа «ДеревоЗначений» в паре с методом Выгрузить() результата запроса по иерархии (ОбходРезультатаЗапроса.ПоГруппировкамСИерархией).
Способ 3. JSON-строка для больших объёмов и обмена
Когда данные нужно передать не только на клиент, но и, например, во внешний веб-сервис или сохранить в файл, удобно сериализовать выборку в JSON. Строка передаётся между клиентом и сервером без ограничений, а на клиенте разбирается обратно.
&НаСервере
Функция ДанныеJSONНаСервере()
// ... заполнение массива структур Данные ...
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
Возврат Запись.Закрыть();
КонецФункции
&НаКлиенте
Процедура Обработать(Команда)
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ДанныеJSONНаСервере());
Данные = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
КонецПроцедуры
Минус подхода: ссылки при сериализации в JSON превращаются в строковые представления, и восстановить их на клиенте в ссылочный тип штатно не получится. Поэтому JSON подходит для плоских данных — строк, чисел, дат, — но не для передачи ссылок, с которыми потом нужно работать как с объектами.
Способ 4. Временное хранилище
Для крупных результатов, которые не нужно сразу отображать, данные можно поместить во временное хранилище на сервере, а на клиент передать только адрес. Клиент обращается к данным по адресу по мере необходимости.
&НаСервере
Функция ПоместитьВХранилищеНаСервере()
Таблица = Запрос.Выполнить().Выгрузить();
Адрес = ПоместитьВоВременноеХранилище(Таблица, УникальныйИдентификаторФормы);
Возврат Адрес;
КонецФункции
Второй параметр — уникальный идентификатор формы — привязывает данные к времени жизни формы: после её закрытия хранилище очистится автоматически. Без этого параметра данные живут до конца сеанса и создают утечки памяти на сервере.
Получить данные обратно можно только на сервере методом ПолучитьИзВременногоХранилища(Адрес). То есть схема работает в связке: клиент хранит адрес и вызывает серверные процедуры, которые извлекают данные по этому адресу порциями или целиком.
Сравнение способов передачи
Универсально, сохраняет ссылки. Плохо масштабируется на десятки тысяч строк из-за накладных расходов
Штатный путь для отображения данных пользователю. Требует совпадения колонок, не годится для промежуточных вычислений
Подходит для обмена и плоских данных. Ссылки теряют тип, превращаясь в строки
Для больших результатов и поэтапной обработки. Требует серверного вызова для чтения данных
Как выбрать способ под задачу
Выбор определяется двумя вопросами: куда идут данные и какого они объёма. Если данные нужно показать пользователю в таблице формы — всегда используйте реквизит формы, это единственный вариант без ручной сериализации. Если данные нужны клиенту для обхода в цикле, проверок или заполнения других объектов — массив структур.
Если строк больше нескольких тысяч и на клиенте они нужны лишь частями — временное хранилище. Если результат уходит во внешнюю систему или файл — JSON. Смешивать способы допустимо: например, поместить таблицу в хранилище, а на клиент вернуть массив структур с первыми строками для предпросмотра.
Как выбрать действие
Частые ошибки и как их исправить
Самая частая ошибка — попытка вернуть выборку или результат запроса напрямую: Возврат Запрос.Выполнить().Выбрать();. Платформа выдаёт ошибку сериализации. Решение — всегда материализовать данные: Выгрузить() в таблицу или обход с заполнением структур.
Вторая типичная проблема — передача в структуре несериализуемых значений. Например, поле запроса содержит значение типа «ХранилищеЗначения» или результат функции, возвращающей объект. Перед добавлением в структуру такие значения нужно преобразовать: из хранилища извлечь данные на сервере, объект заменить ссылкой или строковым представлением.
Третья ошибка — вызов серверной процедуры без контекста формы из клиентского модуля с директивой &НаКлиенте в общем модуле, где серверный вызов запрещён. Проверяйте флажки «Сервер» и «Вызов сервера» у общего модуля, а также что функция объявлена с Экспорт.
Как проверить результат
После внедрения кода проверьте три вещи. Во-первых, количество строк: выполните тот же запрос в консоли запросов и сравните число записей с размером массива или таблицы на клиенте. Расхождение указывает на ошибку в цикле обхода или на дубли при соединениях.
Во-вторых, типы полей: убедитесь, что даты остались датами, а ссылки — ссылками, если планируется открытие объектов по двойному клику. В-третьих, время выполнения: замерьте длительность серверного вызова на реальном объёме данных. Если передача занимает секунды, уменьшите набор полей или смените способ.
Как проверить результат
Совпадает с результатом запроса в консоли
Строк меньше или больше — ошибка в цикле или дубли от соединения
Ссылки открываются, даты сортируются как даты
Ссылки стали строками, даты — текстом
Форма реагирует без заметной паузы
Задержка в секунды — слишком большой объём или лишние поля
Что важно запомнить
Выборку и результат запроса передать нельзя — данные нужно материализовать. Для отображения на форме используйте реквизит ТаблицаЗначений, для обхода на клиенте — массив структур, для больших объёмов — временное хранилище, для обмена — JSON. Следите за сериализуемостью типов и объёмом передаваемых данных.
Частые вопросы
Можно ли вернуть ТаблицуЗначений из серверной функции напрямую?
Технически платформа позволяет вернуть таблицу значений из функции, вызванной с клиента в контексте формы, однако это создаёт полный трафик сериализации таблицы и не работает во всех сценариях (например, из внешних соединений). Надёжнее использовать реквизит формы, массив структур или временное хранилище.
Почему возникает ошибка «Тип не может быть сериализован»?
В возвращаемом значении есть объект, не имеющий сериализуемого представления: выборка запроса, объект справочника или документа (не ссылка), ComОбъект, ХранилищеЗначения в некоторых контекстах. Найдите это поле и замените его ссылкой, строкой или извлечёнными на сервере данными.
Как передать на клиент только часть строк, например первые 100?
Ограничьте выборку на уровне запроса конструкцией ВЫБРАТЬ ПЕРВЫЕ 100 — это эффективнее, чем прерывать цикл обхода, потому что сервер СУБД не будет выбирать лишние данные. Полный результат при этом можно поместить во временное хранилище для последующей дозагрузки.
Работает ли передача массива структур в тонком и веб-клиенте?
Да, массивы, структуры и примитивные типы сериализуются во всех клиентах управляемого приложения. Ограничения касаются только несериализуемых объектных типов, и они одинаковы для тонкого, толстого и веб-клиента.
Что делать, если в выборке есть поле с картинкой или файлом?
Двоичные данные сериализуются, но сильно раздувают трафик. Правильнее передать на клиент навигационную ссылку на объект или адрес во временном хранилище, а файл или картинку получать отдельным вызовом по требованию — например, при открытии строки.
Можно ли обойтись без серверного вызова вообще?
Если данные уже есть в реквизитах формы — да, работайте с ними на клиенте напрямую. Но любой новый запрос к базе требует серверного контекста: динамические списки формы выполняют запросы автоматически, и часто правильнее настроить динамический список, чем вручную передавать выборку.