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