Как выгрузить данные из табличного документа в XML в 1С 8.3?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки часто возникает необходимость выгрузить сформированный отчет или печатную форму в структурированный формат XML. Однако если мы обратимся к стандартному методу ТабличныйДокумент.Записать(), то обнаружим, что среди доступных значений перечисления ТипФайлаТабличногоДокумента формат XML отсутствует. Напрямую сохранить документ в XML через платформенный экспорт невозможно.

В этой ситуации разработчики обычно выбирают промежуточное преобразование: сначала извлекают данные из табличного документа в ТаблицаЗначений, а затем выгружают её в XML. Разберем по шагам, с какими подводными камнями можно столкнуться при использовании объекта ПостроительЗапроса, почему возникает синтаксическая ошибка и как корректно решить задачу выгрузки несколькими способами.

Причина ошибки «Ожидается выражение ВЫБРАТЬ» при работе с ПостроителемЗапроса

Посмотрим на типовую попытку преобразовать табличный документ в таблицу значений через объект ПостроительЗапроса:


ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
ПЗ.ЗаполнитьНастройки();
ПЗ.Выполнить();
ТЗ = ПЗ.Результат.Выгрузить();

При выполнении этого кода на строке получения результата нередко возникает исключение: {(1, 1)}: Ожидается выражение "ВЫБРАТЬ". Выясним причину этой ошибки.

Когда мы передаем область ячеек в ОписаниеИсточникаДанных, платформа автоматически интерпретирует самую первую строку переданной области как названия будущих колонок. В реальном отчете вверху документа обычно выводятся заголовок отчета, период, параметры отбора, пустые строки и объединенные ячейки. В результате автоматическое формирование текста запроса завершается ошибкой, так как платформа не может составить валидные идентификаторы колонок для инструкции ВЫБРАТЬ.

Решение 1: Точное позиционирование области данных для ПостроителяЗапроса

Чтобы метод сработал корректно, нам не нужно вручную редактировать файл или удалять строки. Достаточно программно передать в ОписаниеИсточникаДанных координаты прямоугольной области, начинающейся строго со строки заголовков колонок таблицы:


// Номер строки, в которой расположены названия колонок таблицы
НомерСтрокиШапки = 5; 
НомерПервойКолонки = 1;

ОбластьТаблицы = ТабДок.Область(
    НомерСтрокиШапки, 
    НомерПервойКолонки, 
    ТабДок.ВысотаТаблицы, 
    ТабДок.ШиринаТаблицы
);

ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьТаблицы);
ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
ПЗ.ЗаполнитьНастройки();
ПЗ.Выполнить();

ТаблицаДанных = ПЗ.Результат.Выгрузить();

При этом важно проследить, чтобы в строке шапки не было объединенных ячеек, пробелов и специальных символов в именах колонок.

Решение 2: Выгрузка напрямую из СКД (если отчет построен на схеме компоновки)

Если отчет изначально строится с помощью Системы компоновки данных, парсить готовый табличный документ вообще не требуется. Мы можем вывести данные напрямую в коллекцию значений. Это исключает любые ошибки форматирования и сохраняет исходные типы данных (ссылки, даты, числа):


// Получаем настройки и макет компоновки
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанных"));

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);

// Выводим не в ТабличныйДокумент, а в коллекцию значений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТаблицаДанных = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаДанных);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Решение 3: Прямой обход ячеек документа циклом

Если структура шапки отчета сложная (многоуровневые или объединенные заголовки), наиболее надежным и управляемым способом является ручной обход строк. При этом обратим внимание на важное свойство ячеек: если табличный документ формировался программно или через СКД, ячейка может содержать типизированное значение, а не только строку представления:


ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Номенклатура");
ТаблицаДанных.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаДанных.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));

Для НомерСтроки = 6 По ТабДок.ВысотаТаблицы Цикл
    ЯчейкаНоменклатура = ТабДок.Область(НомерСтроки, 1);
    
    // Пропускаем пустые строки или подвалы
    Если ПустаяСтрока(ЯчейкаНоменклатура.Текст) Тогда
        Продолжить;
    КонецЕсли;
    
    НоваяСтрока = ТаблицаДанных.Добавить();
    
    // Считываем типизированное значение, если оно доступно
    Если ЯчейкаНоменклатура.СодержитЗначение Тогда
        НоваяСтрока.Номенклатура = ЯчейкаНоменклатура.Значение;
    Иначе
        НоваяСтрока.Номенклатура = ЯчейкаНоменклатура.Текст;
    КонецЕсли;
    
    НоваяСтрока.Количество = ТабДок.Область(НомерСтроки, 2).Значение;
    НоваяСтрока.Сумма = ТабДок.Область(НомерСтроки, 3).Значение;
КонецЦикла;

Выгрузка полученных данных в XML

Когда таблица значений получена, рассмотрим способы выгрузки её содержимого в XML-файл.

Способ А. Быстрая сериализация через XDTO

Объект ТаблицаЗначений поддерживает стандартную XDTO-сериализацию. Если формат XML не регламентирован сторонней системой и нужен просто структурированный перенос данных, мы можем сериализовать всю таблицу одной строкой кода:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\data.xml", "UTF-8");

СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ТаблицаДанных);

ЗаписьXML.Закрыть();

Способ Б. Ручное формирование заданной XML-структуры

Если файл отправляется во внешнюю систему с жесткими требованиями к тегам и атрибутам, используем последовательную запись узлов через ЗаписьXML:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\report.xml", "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();

ЗаписьXML.ЗаписатьНачалоЭлемента("ReportData");

Для Каждого СтрДанных Из ТаблицаДанных Цикл
    ЗаписьXML.ЗаписатьНачалоЭлемента("Row");
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("Item");
    ЗаписьXML.ЗаписатьТекст(Строка(СтрДанных.Номенклатура));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьНачалоЭлемента("Quantity");
    ЗаписьXML.ЗаписатьТекст(Формат(СтрДанных.Количество, "ЧГ=0"));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьКонецЭлемента(); // Row
КонецЦикла;

ЗаписьXML.ЗаписатьКонецЭлемента(); // ReportData
ЗаписьXML.Закрыть();

Способ В. Полная сериализация ТабличногоДокумента

Если задача заключается не в извлечении бизнес-данных, а в сохранении исходного макета табличного документа (со всеми стилями, рамками, цветами и объединениями ячеек), сам объект ТабличныйДокумент также напрямую сериализуется через XDTO:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\SpreadsheetDocument.xml", "UTF-8");

СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ТабДок);

ЗаписьXML.Закрыть();
← На главную