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