При интеграции 1С с внешними информационными системами через Web-сервисы или при разборе XML/JSON-документов разработчики часто сталкиваются с объектами модели XDTO (XML Data Transfer Objects). Одним из таких объектов является СписокXDTO. Это специализированная коллекция, предназначенная для представления списочных данных XML-схемы.
Рано или поздно возникает прикладная задача: быстро и наглядно вывести содержимое полученного списка на форму для анализа пользователем или выполнения интерактивных действий. Попытка напрямую привязать СписокXDTO к элементу формы (например, к табличному полю в обычном приложении или к динамическому списку/таблице в управляемом приложении) приводит к неудаче. Система просто не понимает этот тип данных в контексте интерфейса. В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько эффективных способов вывода данных из СписокXDTO на форму.
Давайте проанализируем, почему стандартные и привычные механизмы 1С пасуют перед XDTO. Первое, что пробуют сделать разработчики, — это связать элемент управления формы с типом СписокЗначений и напрямую присвоить ему СписокXDTO. Но СписокXDTO — это не коллекция простых значений 1С, а сложная иерархическая структура метаданных. Он хранит в себе элементы типа ОбъектXDTO или ЗначениеXDTO, которые не сопоставляются напрямую с типами платформы.
Вторая частая попытка — ручной обход списка циклом с использованием функции ЗаполнитьЗначенияСвойств. Разработчик создает таблицу значений, добавляет строки и пытается перенести свойства из XDTO. Однако на выходе часто получаются пустые строки. Выясним, почему так происходит:
ЗаполнитьЗначенияСвойств(Получатель, Источник) необходимо, чтобы в приемнике (строке таблицы значений) уже существовали колонки с именами, которые символ в символ совпадают со свойствами объекта-источника XDTO.ОбъектXDTO или СписокXDTO), а колонка таблицы значений жестко типизирована (например, как строка или число), 1С не сможет автоматически привести типы и запишет пустое значение либо вызовет ошибку.Чтобы решить эту проблему, нам потребуется преобразовать данные XDTO в понятные для интерфейса платформы структуры. Разберем три основных практических метода решения этой задачи.
Рассмотрим самый надежный способ, который не требует жесткого кодирования структуры данных. Мы напишем универсальную функцию, которая сама проанализирует структуру первого элемента в СписокXDTO, динамически создаст необходимые колонки в ТаблицаЗначений без привязки к конкретным типам, а затем перенесет все данные.
Посмотрим на пример реализации такой функции:
Функция СписокXDTOВТаблицуЗначений(СписокXDTO) Export
Таблица = Новый ТаблицаЗначений;
Если СписокXDTO.Количество() = 0 Тогда
Возврат Таблица;
КонецЕсли;
// 1. Динамически определяем структуру колонок по первому элементу
ПервыйЭлемент = СписокXDTO.Получить(0);
Если ТипЗнч(ПервыйЭлемент) = Тип("ОбъектXDTO") Тогда
Для Каждого Свойство Из ПервыйЭлемент.Свойства() Цикл
// Создаем колонку без явного указания типа, чтобы избежать конфликтов приведения типов
Таблица.Колонки.Добавить(Свойство.Имя);
КонецЦикла;
Иначе
// Если в списке находятся простые значения, создаем одну универсальную колонку
Таблица.Колонки.Добавить("Значение");
КонецЕсли;
// 2. Заполняем созданную таблицу данными
Для Каждого Элемент Из СписокXDTO Цикл
НоваяСтрока = Таблица.Добавить();
Если ТипЗнч(Элемент) = Тип("ОбъектXDTO") Тогда
Для Каждого Колонка Из Таблица.Колонки Цикл
ИмяСвойства = Колонка.Имя;
ЗначениеСвойства = Элемент[ИмяСвойства];
// Обрабатываем возможные вложенные структуры XDTO
Если ТипЗнч(ЗначениеСвойства) = Тип("ОбъектXDTO") Тогда
НоваяСтрока[ИмяСвойства] = "[Объект XDTO]";
ИначеЕсли ТипЗнч(ЗначениеСвойства) = Тип("СписокXDTO") Тогда
НоваяСтрока[ИмяСвойства] = "[Список XDTO]";
Иначе
// Для простых типов записываем значение напрямую
НоваяСтрока[ИмяСвойства] = ЗначениеСвойства;
КонецЕсли;
КонецЦикла;
Иначе
// Для списка простых значений (ЗначениеXDTO)
НоваяСтрока.Значение = Элемент;
КонецЕсли;
КонецЦикла;
Возврат Таблица;
КонецФункции
Проанализируем этот код. Мы создаем колонки таблицы значений динамически, основываясь на свойствах объекта XDTO. Обратите внимание, что мы не передаем второй параметр в метод Таблица.Колонки.Добавить(). Это позволяет колонке принимать значения абсолютно любого типа, включая другие объекты и списки. Если в процессе обхода мы натыкаемся на вложенную структуру, мы заменяем ее текстовым представлением, предотвращая падение платформы или некорректное преобразование типов.
Если структура вашего XDTO сложная, многоуровневая и содержит сильные вложенности, ручной обход может показаться громоздким. В современных версиях платформы 1С (начиная с 8.3) есть изящный обходной путь — использование JSON-сериализации.
Платформа умеет автоматически трансформировать сложные структуры XDTO в JSON-строку, а встроенные механизмы чтения JSON с легкостью превращают эту строку во взаимно вложенные стандартные коллекции 1С: Массив и Структура. С ними работать в коде интерфейса в разы проще.
Разберем по шагам, как реализовать этот алгоритм:
// Предположим, у нас есть ИсходныйОбъектXDTO, содержащий наш СписокXDTO
// Шаг 1: Записываем объект XDTO в строку JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
// Сериализуем объект с помощью встроенной фабрики XDTO
ФабрикаXDTO.ЗаписатьJSON(ЗаписьJSON, ИсходныйОбъектXDTO);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Шаг 2: Читаем полученную строку обратно в коллекции 1С
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
// На выходе получаем Массив Структур, полностью повторяющий структуру XDTO
ДанныеФормы = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Этот метод невероятно эффективен. Всего за несколько строк кода мы избавляемся от специфических объектов XDTO, получая взамен чистые структуры и массивы, которые можно напрямую связывать с реквизитами управляемой формы или легко выгружать в дерево значений.
Если вы ведете разработку в типовой конфигурации 1С, созданной на базе Библиотеки Стандартных Подсистем (БСП), вам не нужно изобретать велосипед и писать собственные конвертеры. В БСП уже встроены высокоэффективные оптимизированные механизмы для работы со схемами XDTO.
В частности, вы можете воспользоваться общим модулем ОбменДаннымиXDTOСервер. Функция ОбъектXDTOВСтруктуру() берет на себя всю рутину по рекурсивному разбору элементов XDTO:
// Пример использования готового функционала БСП
СтруктураДанных = ОбменДаннымиXDTOСервер.ОбъектXDTOВСтруктуру(ВашОбъектXDTO);
На выходе вы получаете готовую структуру 1С, которую можно моментально обойти стандартными методами платформы и заполнить экранную таблицу.
После того как мы успешно преобразовали СписокXDTO в стандартную ТаблицаЗначений одним из приведенных выше способов, нам необходимо отобразить ее пользователю. Рассмотрим, как правильно сделать это в обычном приложении (неуправляемые формы), так как именно там чаще всего возникают сложности с динамическим обновлением элементов.
Для вывода нам понадобится элемент управления Табличное поле. Выполним следующие шаги:
ТабличноеПолеДанных).ТаблицаЗначений (но ни в коем случае не СписокЗначений!).Рассмотрим пример программного вывода на форму:
// 1. Получаем подготовленную таблицу значений из нашего списка XDTO
ТаблицаДляВывода = СписокXDTOВТаблицуЗначений(ПолученныйСписокXDTO);
// 2. Связываем полученную таблицу значений с интерфейсным элементом формы
ЭлементыФормы.ТабличноеПолеДанных.Значение = ТаблицаДляВывода;
// 3. Самый важный шаг: заставляем элемент формы перестроить визуальные колонки
ЭлементыФормы.ТабличноеПолеДанных.СоздатьКолонки();
Обратите особое внимание на метод СоздатьКолонки(). Если его не вызвать, то табличное поле на форме останется пустым, даже если в свойстве Значение находится заполненная таблица. Этот метод анализирует структуру колонок переданной таблицы значений и автоматически создает соответствующие им визуальные столбцы на форме. Пользователь мгновенно увидит все данные на экране.
После этого вы сможете полноценно работать с интерактивными свойствами таблицы. Например, перехватив событие Выбор или ПриАктивизацииСтроки у табличного поля, вы легко сможете получить значение текущей строки (например, уникальный идентификатор GUID) и выполнить по нему любые дальнейшие действия в системе.