При динамической выгрузке метаданных и данных справочников в формат XML разработчики часто сталкиваются с ситуацией, когда структура метаданных (имена реквизитов и табличных частей) успешно записывается в файл, а сами строки табличной части либо не выгружаются вовсе, либо вызывают синтаксические ошибки при выполнении запроса. Разберем по шагам, почему возникают такие проблемы и как их правильно исправить.
Выясним причину ошибок, которые привели к падению запроса и отсутствию данных в сформированном XML-файле:
ИЗ. При обращении к табличной части справочника в языке запросов 1С используется составное имя с точкой: Справочник.<ИмяСправочника>.<ИмяТабличнойЧасти>. Если программно склеивать имя без разделителя, получится несуществующая таблица вида Справочник.СотрудникиТрудоваяДеятельность, что вызывает ошибку: «Таблица не найдена». Если же запрос строится к основной таблице справочника, платформа выдает исключение: «Поле не найдено», так как реквизиты табличной части не принадлежат шапке.Получить() у выборки. Объект ВыборкаИзРезультатаЗапроса не имеет метода Получить(Индекс), а его метод Количество() возвращает общее количество строк в выборке, а не число колонок. Обращение к элементам по числовому индексу внутри блока Попытка ... Исключение молча завершалось ошибкой, переменная оставалась равной Неопределено, и выгрузка данных попросту пропускалась.Если Знч = Тип("Строка") Тогда игнорирует даты, числа, ссылки и булевы значения, отсекая значительную часть информации.Реквизит.Имя оставалась после завершения предыдущего цикла обхода метаданных, из-за чего атрибут XML получал некорректное имя последнего реквизита.Рассмотрим, как правильно сформировать текст запроса к каждой табличной части справочника. Имя таблицы должно обязательно содержать точку между именем справочника и именем табличной части. Также для связи со строками конкретного элемента следует выбирать поле Ссылка.
Посмотрим на пример правильного формирования запроса:
Для Каждого ТЧ Из Метаданные.Справочники.Сотрудники.ТабличныеЧасти Цикл
ТекстПолей = "";
Для Каждого Реквизит Из ТЧ.Реквизиты Цикл
ТекстПолей = ТекстПолей + ", Справ." + Реквизит.Имя;
КонецЦикла;
ТекстЗапроса =
"ВЫБРАТЬ
| Справ.Ссылка КАК Ссылка,
| Справ.НомерСтроки КАК НомерСтроки" + ТекстПолей + "
|ИЗ
| Справочник.Сотрудники." + ТЧ.Имя + " КАК Справ";
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
РезультатЗапроса = Запрос.Выполнить();
КонецЦикла;
Разберем, как получить значения полей из выборки. К полям выборки запроса можно обращаться по их строковым именам через оператор [], например Выборка[Реквизит.Имя].
Для преобразования любых типов данных платформы 1С (чисел, дат, ссылок, булево) в стандартный текстовый формат XML следует использовать глобальную функцию XMLСтрока(). Она корректно преобразует ссылки в GUID, даты в формат ISO (ГГГГ-ММ-ДД), а логические значения в true/false.
Объединим обход метаданных, выполнение запроса и сериализацию данных в единый рабочий пример:
ИмяСправочника = "Сотрудники";
ОбъектМетаданных = Метаданные.Справочники[ИмяСправочника];
Для Каждого ТЧ Из ОбъектМетаданных.ТабличныеЧасти Цикл
Файл.ЗаписатьНачалоЭлемента("ТабличнаяЧасть");
Файл.ЗаписатьАтрибут("Имя", ТЧ.Имя);
Файл.ЗаписатьАтрибут("Синоним", ТЧ.Синоним);
// 1. Формируем список полей выборки
ТекстПолей = "";
Для Каждого Реквизит Из ТЧ.Реквизиты Цикл
ТекстПолей = ТекстПолей + ", Справ." + Реквизит.Имя + " КАК " + Реквизит.Имя;
КонецЦикла;
// 2. Формируем запрос с обязательной точкой перед именем ТЧ
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Справ.Ссылка.УникальныйИдентификатор КАК ИдентификаторЭлемента,
| Справ.НомерСтроки КАК НомерСтроки" + ТекстПолей + "
|ИЗ
| Справочник." + ИмяСправочника + "." + ТЧ.Имя + " КАК Справ";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// 3. Обходим строки и реквизиты
Пока Выборка.Следующий() Цикл
Файл.ЗаписатьНачалоЭлемента("Строка");
Файл.ЗаписатьАтрибут("НомерСтроки", XMLСтрока(Выборка.НомерСтроки));
Для Каждого Реквизит Из ТЧ.Реквизиты Цикл
Значение = Выборка[Реквизит.Имя];
Если Значение <> Неопределено Тогда
Файл.ЗаписатьНачалоЭлемента("Реквизит");
Файл.ЗаписатьАтрибут("Имя", Реквизит.Имя);
Файл.ЗаписатьАтрибут("Значение", XMLСтрока(Значение));
Файл.ЗаписатьКонецЭлемента();
КонецЕсли;
КонецЦикла;
Файл.ЗаписатьКонецЭлемента(); // Строка
КонецЦикла;
Файл.ЗаписатьКонецЭлемента(); // ТабличнаяЧасть
КонецЦикла;
Проанализируем другие стандартные механизмы 1С, которые позволяют избежать ручного формирования тегов XML:
ЗаписатьXML(). Если необходимо выгрузить объект целиком (шапку и все его табличные части), проще всего сериализовать объект платформенным методом:
СправочникОбъект = ВыбраннаяСсылка.ПолучитьОбъект();
ЗаписатьXML(Файл, СправочникОбъект, НазначениеТипаXML.Явное);
СериализаторXDTO. Если табличную часть нужно выгрузить отдельно в виде таблицы значений:
ТаблицаТЧ = ВыбраннаяСсылка.ТрудоваяДеятельность.Выгрузить();
СериализаторXDTO.ЗаписатьXML(Файл, ТаблицаТЧ);