При интеграции 1С со сторонними системами часто возникает задача сформировать файл формата XML на основании данных отчета системы компоновки данных (СКД). Нередко начинающие разработчики пытаются вывести отчет в табличный документ, а затем передать полученный результат напрямую в объект XDTO с помощью функции ЗаполнитьЗначенияСвойств или сталкиваются с ошибками типизации при прямой записи тегов через ЗаписьXML.
В этой статье мы подробно разберем, почему возникают эти ошибки, как правильно получать данные из СКД для программной обработки и как корректно сформировать XML-документ требуемой структуры.
Рассмотрим основные заблуждения и причины сбоев, с которыми сталкиваются разработчики при попытке экспорта отчета:
ТабличныйДокумент и объект XDTO: Метод ЗаполнитьЗначенияСвойств выполняет прямое копирование значений только свойств первого уровня с одинаковыми именами. Объект ТабличныйДокумент содержит ячейки, области и графическое оформление, а не плоский список реквизитов или прикладных бизнес-данных. По этой причине скопировать данные табличного документа в свойства типа XDTO таким способом невозможно.ЗаписатьТекст: Объект ЗаписьXML требует строгого соблюдения типов параметров. Метод ЗаписатьТекст принимает исключительно строковые значения (тип Строка). Передача числа, даты, булевого значения или ссылки напрямую вызывает ошибку времени выполнения.СериализаторXDTO жестко привязано к внутренним типам 1С и схеме пространств имен платформы. При сериализации объектов платформы генерируется большое количество технических служебных атрибутов (например, xsi:type), что недопустимо, если сторонний сервис ожидает строгую кастомную структуру.Если для обмена требуется предоставить XML строго определенного формата без лишних метаданных платформы, наиболее надежным и быстрым решением является последовательная генерация файла с помощью объекта ЗаписьXML.
Разберем по шагам, как выгрузить данные и избежать ошибок типизации. Главное правило — все выгружаемые значения примитивных типов (числа, даты) необходимо приводить к корректному строковому представлению с помощью функции XMLСтрока() или Формат().
Посмотрим на пример кода формирования XML-файла:
// Инициализируем объект записи и настраиваем параметры файла
ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ИмяФайла = "C:\test\REG_" + Формат(ТекущаяДата(), "ДФ=yyyyMMdd_HHmmss") + ".xml";
ЗаписьXML.ОткрытьФайл(ИмяФайла, ПараметрыЗаписиXML);
ЗаписьXML.ЗаписатьОбъявлениеXML();
// Записываем корневой элемент
ЗаписьXML.ЗаписатьНачалоЭлемента("DATAPACKET");
// Пример записи вложенного узла с данными
ЗаписьXML.ЗаписатьНачалоЭлемента("FlightInfo");
// Запись текстового узла с обязательным преобразованием в XML-строку
ЗаписьXML.ЗаписатьНачалоЭлемента("FlightNumber");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(Рейс.FlightNumber));
ЗаписьXML.ЗаписатьКонецЭлемента(); // FlightNumber
ЗаписьXML.ЗаписатьНачалоЭлемента("DepartureDate");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(Рейс.ДатаВылета));
ЗаписьXML.ЗаписатьКонецЭлемента(); // DepartureDate
ЗаписьXML.ЗаписатьКонецЭлемента(); // FlightInfo
// Закрываем корневой элемент и файл
ЗаписьXML.ЗаписатьКонецЭлемента(); // DATAPACKET
ЗаписьXML.Закрыть();
Важное замечание: Глобальная функция XMLСтрока() преобразует переданное значение любого базового типа (число, дата, булево, ссылки) в канонический строковый формат XML (например, даты в формате YYYY-MM-DDThh:mm:ss, а разделители дробной части чисел — в виде точки), что гарантирует валидность создаваемого документа.
Для программной обработки результатов отчета СКД вывод в табличный документ не подходит. Вместо этого настроим процессор вывода на выгрузку в объект ТаблицаЗначений или ДеревоЗначений с помощью ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений.
Рассмотрим пример выполнения компоновки и последующей выгрузки строк отчета в XML:
// 1. Получение схемы и настроек компоновки
СхемаКомпоновкиДанных = Отчеты.ОтчётПоОтгрузкеПростой.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
// 2. Инициализация процессора компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
// 3. Вывод результата в Таблицу Значений
ТаблицаРезультата = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаРезультата);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
// 4. Построчная выгрузка полученной таблицы в XML
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\test\report_data.xml", "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("ReportData");
Для Каждого Стр Из ТаблицаРезультата Цикл
ЗаписьXML.ЗаписатьНачалоЭлемента("Row");
ЗаписьXML.ЗаписатьНачалоЭлемента("Номенклатура");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(Стр.Номенклатура));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("Количество");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(Стр.Количество));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьКонецЭлемента(); // Row
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // ReportData
ЗаписьXML.Закрыть();
Если отчет содержит большой объем строк, выгрузка всей таблицы значений в оперативную память может вызвать повышенное потребление ресурсов сервера. В таких сценариях эффективнее использовать потоковое чтение напрямую из ПроцессорКомпоновкиДанных без создания промежуточных коллекций.
Проанализируем пример потоковой обработки:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\test\stream_report.xml", "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Report");
// Потоковый обход элементов результата компоновки
Пока Истина Цикл
ЭлементРезультата = ПроцессорКомпоновки.СледующийЭлемент();
Если ЭлементРезультата = Неопределено Тогда
Прервать;
КонецЕсли;
// Обрабатываем только строки с записями данных
Если ЭлементРезультата.ТипЭлемента = ТипЭлементаРезультатаКомпоновкиДанных.Интервал
Или ЭлементРезультата.ЗначенияПараметров.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
ЗаписьXML.ЗаписатьНачалоЭлемента("Record");
Для Каждого Параметр Из ЭлементРезультата.ЗначенияПараметров Цикл
ЗаписьXML.ЗаписатьНачалоЭлемента(Параметр.Имя);
ЗаписьXML.ЗаписатьТекст(XMLСтрока(Параметр.Значение));
ЗаписьXML.ЗаписатьКонецЭлемента();
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // Record
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // Report
ЗаписьXML.Закрыть();