В процессе разработки часто возникает необходимость выгрузить сформированный отчет или печатную форму в структурированный формат XML. Однако если мы обратимся к стандартному методу ТабличныйДокумент.Записать(), то обнаружим, что среди доступных значений перечисления ТипФайлаТабличногоДокумента формат XML отсутствует. Напрямую сохранить документ в XML через платформенный экспорт невозможно.
В этой ситуации разработчики обычно выбирают промежуточное преобразование: сначала извлекают данные из табличного документа в ТаблицаЗначений, а затем выгружают её в XML. Разберем по шагам, с какими подводными камнями можно столкнуться при использовании объекта ПостроительЗапроса, почему возникает синтаксическая ошибка и как корректно решить задачу выгрузки несколькими способами.
Посмотрим на типовую попытку преобразовать табличный документ в таблицу значений через объект ПостроительЗапроса:
ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
ПЗ.ЗаполнитьНастройки();
ПЗ.Выполнить();
ТЗ = ПЗ.Результат.Выгрузить();
При выполнении этого кода на строке получения результата нередко возникает исключение: {(1, 1)}: Ожидается выражение "ВЫБРАТЬ". Выясним причину этой ошибки.
Когда мы передаем область ячеек в ОписаниеИсточникаДанных, платформа автоматически интерпретирует самую первую строку переданной области как названия будущих колонок. В реальном отчете вверху документа обычно выводятся заголовок отчета, период, параметры отбора, пустые строки и объединенные ячейки. В результате автоматическое формирование текста запроса завершается ошибкой, так как платформа не может составить валидные идентификаторы колонок для инструкции ВЫБРАТЬ.
Чтобы метод сработал корректно, нам не нужно вручную редактировать файл или удалять строки. Достаточно программно передать в ОписаниеИсточникаДанных координаты прямоугольной области, начинающейся строго со строки заголовков колонок таблицы:
// Номер строки, в которой расположены названия колонок таблицы
НомерСтрокиШапки = 5;
НомерПервойКолонки = 1;
ОбластьТаблицы = ТабДок.Область(
НомерСтрокиШапки,
НомерПервойКолонки,
ТабДок.ВысотаТаблицы,
ТабДок.ШиринаТаблицы
);
ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьТаблицы);
ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
ПЗ.ЗаполнитьНастройки();
ПЗ.Выполнить();
ТаблицаДанных = ПЗ.Результат.Выгрузить();
При этом важно проследить, чтобы в строке шапки не было объединенных ячеек, пробелов и специальных символов в именах колонок.
Если отчет изначально строится с помощью Системы компоновки данных, парсить готовый табличный документ вообще не требуется. Мы можем вывести данные напрямую в коллекцию значений. Это исключает любые ошибки форматирования и сохраняет исходные типы данных (ссылки, даты, числа):
// Получаем настройки и макет компоновки
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, , , Тип("ГенераторМакетаКомпоновкиДанных"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
// Выводим не в ТабличныйДокумент, а в коллекцию значений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТаблицаДанных = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаДанных);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Если структура шапки отчета сложная (многоуровневые или объединенные заголовки), наиболее надежным и управляемым способом является ручной обход строк. При этом обратим внимание на важное свойство ячеек: если табличный документ формировался программно или через СКД, ячейка может содержать типизированное значение, а не только строку представления:
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Номенклатура");
ТаблицаДанных.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаДанных.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Для НомерСтроки = 6 По ТабДок.ВысотаТаблицы Цикл
ЯчейкаНоменклатура = ТабДок.Область(НомерСтроки, 1);
// Пропускаем пустые строки или подвалы
Если ПустаяСтрока(ЯчейкаНоменклатура.Текст) Тогда
Продолжить;
КонецЕсли;
НоваяСтрока = ТаблицаДанных.Добавить();
// Считываем типизированное значение, если оно доступно
Если ЯчейкаНоменклатура.СодержитЗначение Тогда
НоваяСтрока.Номенклатура = ЯчейкаНоменклатура.Значение;
Иначе
НоваяСтрока.Номенклатура = ЯчейкаНоменклатура.Текст;
КонецЕсли;
НоваяСтрока.Количество = ТабДок.Область(НомерСтроки, 2).Значение;
НоваяСтрока.Сумма = ТабДок.Область(НомерСтроки, 3).Значение;
КонецЦикла;
Когда таблица значений получена, рассмотрим способы выгрузки её содержимого в 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.Закрыть();