При программном формировании XML-файлов с помощью объекта ЗаписьXML разработчики часто сталкиваются с исключением: «Ошибочный порядок записи XML» на этапе вызова метода ЗаписатьНачалоЭлемента(). Эта ситуация особенно характерна для процедур циклической выгрузки нескольких документов или справочников в один файл.
Разберем детально, почему возникает данная ошибка, как устроен механизм потоковой записи XML в платформе 1С:Предприятие и как правильно организовать структуру кода.
Основная причина появления ошибки заключается в нарушении спецификации стандарта XML и логики работы потокового объекта ЗаписьXML. Выделим ключевые факторы:
ЗаписатьНачалоЭлемента() на верхнем уровне вызовет ошибку, так как после закрытия первого корневого тега документ считается завершенным.ЗаписьXML.Закрыть() вызывается внутри процедуры обработки отдельного документа, то при переходе к следующему объекту поток уже закрыт, и любая запись в него невозможна.ЗаписатьНачалоЭлемента() и ЗаписатьКонецЭлемента() (в том числе во вложенных вспомогательных процедурах сериализации) нарушает внутренний стек тегов.Рассмотрим правильную архитектуру формирования XML-файла при обходе массива или выборки документов. Разделим уровни ответственности: открытие потока, запись единого корневого тега, его закрытие и закрытие файла выполняются снаружи цикла.
Посмотрим на пример управляющей процедуры, которая инициализирует XML и управляет процессом:
Процедура ВыгрузитьДокументыВXML(МассивДокументов, ПутьКФайлу)
ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ЗаписьXML.ОткрытьФайл(ПутьКФайлу, ПараметрыЗаписи);
// 1. Открываем единый корневой элемент файла
ЗаписьXML.ЗаписатьНачалоЭлемента("ФайлОбмена");
// 2. В цикле выгружаем каждый документ как дочерний элемент корня
Для Каждого ТекущийДокумент Из МассивДокументов Цикл
ВыгрузитьОдинДокумент(ЗаписьXML, ТекущийДокумент);
КонецЦикла;
// 3. Закрываем корневой элемент
ЗаписьXML.ЗаписатьКонецЭлемента();
// 4. Завершаем работу с файлом
ЗаписьXML.Закрыть();
КонецПроцедуры
Теперь проанализируем процедуру выгрузки единичного документа. Обратим внимание, что она не должна содержать вызов ЗаписьXML.Закрыть():
Процедура ВыгрузитьОдинДокумент(ЗаписьXML, Данные)
// Открываем узел конкретного документа
ЗаписьXML.ЗаписатьНачалоЭлемента("DocumentObject.ПоступлениеТоваровУслуг.Вручную");
ЗаписатьXML(ЗаписьXML, Данные.Ссылка, "Ref", НазначениеТипаXML.Явное);
ЗаписатьXML(ЗаписьXML, Данные.Номер, "Номер", НазначениеТипаXML.Явное);
ЗаписатьXML(ЗаписьXML, Данные.Дата, "Дата", НазначениеТипаXML.Явное);
ЗаписатьXML(ЗаписьXML, Данные.СуммаДокумента, "СуммаДокумента", НазначениеТипаXML.Явное);
// Выгрузка табличной части
ТЧ = Данные.Товары.Выгрузить();
Для Каждого СтрокаТЧ Из ТЧ Цикл
ЗаписьXML.ЗаписатьНачалоЭлемента("DocumentTabularSectionRow.ПоступлениеТоваровУслуг.Товары.Вручную");
ЗаписатьXML(ЗаписьXML, СтрокаТЧ.Номенклатура, "Номенклатура", НазначениеТипаXML.Явное);
ЗаписатьXML(ЗаписьXML, СтрокаТЧ.Количество, "Количество", НазначениеТипаXML.Явное);
ЗаписатьXML(ЗаписьXML, СтрокаТЧ.Сумма, "Сумма", НазначениеТипаXML.Явное);
ЗаписьXML.ЗаписатьКонецЭлемента(); // Закрываем строку ТЧ
КонецЦикла;
// Закрываем узел текущего документа
ЗаписьXML.ЗаписатьКонецЭлемента();
КонецПроцедуры
Объект ЗаписьXML работает по принципу потоковой однонаправленной записи. Чтобы избежать ошибок порядка записи, придерживаемся следующих правил:
ЗаписатьНачалоЭлемента() должен строго приходиться один вызов ЗаписатьКонецЭлемента().Попытка ... Исключение, чтобы в случае ошибки выполнения поток ЗаписьXML не оставался заблокированным в операционной системе.Если структура файла сложная или выгружаются иерархические объекты с глубокой вложенностью, можно рассмотреть альтернативные механизмы платформы 1С: