Как исправить ошибку «Ошибочный порядок записи XML» в 1С

Программист 1С v8.3 (Обычные формы) Торговля и дистрибуция
← На главную

Работа с XML-файлами — одна из наиболее частых задач при интеграции решений на платформе 1С с внешними системами, интернет-магазинами и государственными сервисами. Для быстрой и ресурсоэффективной генерации XML-документов разработчики используют специализированный объект ЗаписьXML. Однако при его использовании программисты нередко сталкиваются со стандартным, но не всегда понятным системным сообщением: «Ошибочный порядок записи XML».

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем реальный практический кейс с форума разработчиков 1С, исправим некорректный код и рассмотрим другие распространенные сценарии, которые приводят к нарушению структуры XML-документа.

Почему возникает ошибка порядка записи?

Для начала проанализируем архитектурные особенности работы объекта ЗаписьXML. В отличие от объектных моделей парсинга (таких как DOM), данный объект реализует последовательную потоковую запись данных непосредственно в файл или текстовый буфер. Он не хранит всю иерархическую структуру документа в оперативной памяти компьютера. Это означает, что запись происходит «на лету» в жестко заданном направлении.

Поскольку запись идет строго последовательно, платформа 1С на каждом шаге проверяет соответствие выполняемого действия правилам XML-стандарта. Любое отступление от спецификации консорциума W3C (например, попытка записать атрибут в закрытый узел, создание второго корневого тега или добавление текста после закрытия элемента) немедленно прерывает процесс выгрузки и вызывает исключение «Ошибочный порядок записи XML».

Разбор практической ситуации с форума

Рассмотрим реальный пример кода, который вызвал затруднение у автора темы. Разработчик пытался сформировать выгрузку товаров в формате YML (Yandex Market Language) для интернет-магазина. Исходный программный код выглядел следующим образом:


ТекПользователь = глЗначениеПеременной("глТекущийПользователь");
КаталогXML = УправлениеПользователями.ПолучитьЗначениеПоУмолчанию(ТекПользователь, "ОсновнойКаталогФайлов");

ЗаписьХМL = Новый ЗаписьXML;
ЗаписьХМL.ОткрытьФайл(КаталогXML+"\"+ТекущаяДата()+".xml","UTF-8");
ЗаписьХМL.ЗаписатьТипДокумента("yml_catalog", "shops.dtd", "");

ЗаписьХМL.ЗаписатьНачалоЭлемента("yml_catalog");
ЗаписьХМL.ЗаписатьАтрибут("date", Строка(Формат(КонецДня(ТекущаяДата()) - 3539, "ДФ=yyyy-MM-dd")+" "+Формат(КонецДня(ТекущаяДата()) - 3539, "ДФ=ЧЧ")+":"+Формат(КонецДня(ТекущаяДата()) - 3539, "ДФ=мм")));
ЗаписьХМL.ЗаписатьКонецЭлемента(); // Ошибка закладывается здесь!

// Начинаем обход результата запроса
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    ЗаписьХМL.ЗаписатьНачалоЭлемента("shop"); // Здесь происходит аварийное завершение работы

Выясним причину сбоя, внимательно проследив за структурой выгрузки. Обратите внимание на вызовы методов открытия и закрытия XML-тегов:

  1. Сначала мы открываем корневой тег документа при помощи конструкции ЗаписьХМL.ЗаписатьНачалоЭлемента("yml_catalog").
  2. Затем мы успешно записываем в него служебный атрибут даты.
  3. Сразу после этого в коде вызывается инструкция ЗаписьХМL.ЗаписатьКонецЭлемента(). На этом этапе корневой узел yml_catalog полностью закрывается. Для потокового писателя XML это означает, что формирование структуры текущего документа завершено. В файле создана законченная XML-структура.
  4. Далее программа переходит к циклу обработки выборки данных и пытается вызвать метод ЗаписьХМL.ЗаписатьНачалоЭлемента("shop"). Платформа 1С обнаруживает, что корневой элемент уже закрыт, а разработчик пытается добавить еще один элемент верхнего уровня shop. В спецификации XML строго определено: у документа может быть только один корень. Платформа пресекает некорректное действие и выбрасывает исключение.

Пошаговое исправление структуры выгрузки

Чтобы исправить эту логическую ошибку, нам необходимо скорректировать жизненный цикл корневого тега. Элемент yml_catalog должен закрываться только после того, как все внутренние узлы (включая элементы shop) будут полностью записаны в файл. То есть вызов метода закрытия тега необходимо перенести в самый конец алгоритма — за пределы цикла выборки данных.

Посмотрим на пример правильной структуры программного кода:


ТекПользователь = глЗначениеПеременной("глТекущийПользователь");
КаталогXML = УправлениеПользователями.ПолучитьЗначениеПоУмолчанию(ТекПользователь, "ОсновнойКаталогФайлов");

ЗаписьХМL = Новый ЗаписьXML;
ЗаписьХМL.ОткрытьФайл(КаталогXML + "\" + Формат(ТекущаяДата(), "ДФ=yyyyMMdd_ЧЧммсс") + ".xml", "UTF-8");

// 1. Рекомендуется записывать объявление XML в начале файла
ЗаписьХМL.ЗаписатьОбъявлениеXML();
ЗаписьХМL.ЗаписатьТипДокумента("yml_catalog", "shops.dtd", "");

// 2. Открываем корневой элемент каталога
ЗаписьХМL.ЗаписатьНачалоЭлемента("yml_catalog");
ЗаписьХМL.ЗаписатьАтрибут("date", Формат(ТекущаяДата(), "ДФ='yyyy-MM-dd HH:mm'"));

// 3. Открываем обязательный контейнер shop внутри каталога
ЗаписьХМL.ЗаписатьНачалоЭлемента("shop");

// 4. Запускаем цикл обхода выборки данных из БД
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    
    // Записываем карточку товара (подчиненный узел)
    ЗаписьХМL.ЗаписатьНачалоЭлемента("offer");
    ЗаписьХМL.ЗаписатьАтрибут("id", Строка(ВыборкаДетальныеЗаписи.Идентификатор));
    
    ЗаписьХМL.ЗаписатьНачалоЭлемента("name");
    ЗаписьХМL.ЗаписатьТекст(ВыборкаДетальныеЗаписи.Наименование);
    ЗаписьХМL.ЗаписатьКонецЭлемента(); // Закрываем "name"
    
    ЗаписьХМL.ЗаписатьНачалоЭлемента("price");
    ЗаписьХМL.ЗаписатьТекст(Строка(ВыборкаДетальныеЗаписи.Цена));
    ЗаписьХМL.ЗаписатьКонецЭлемента(); // Закрываем "price"
    
    ЗаписьХМL.ЗаписатьКонецЭлемента(); // Закрываем "offer"
    
КонецЦикла;

// 5. Последовательно закрываем открытые теги верхнего уровня в обратном порядке
ЗаписьХМL.ЗаписатьКонецЭлемента(); // Закрываем "shop"
ЗаписьХМL.ЗаписатьКонецЭлемента(); // Закрываем корневой "yml_catalog"

// 6. Обязательно закрываем поток записи для сохранения изменений в файле
ЗаписьХМL.Закрыть();

Проанализировав исправленный вариант, мы видим, что теперь все дочерние узлы offer корректно вкладываются внутрь родительских контейнеров shop и yml_catalog. Запись завершается без ошибок.

Другие распространенные причины ошибки в практике 1С

Неправильная последовательность закрытия тегов при циклах — это частая, но далеко не единственная причина сбоя. Рассмотрим другие ситуации, с которыми регулярно сталкиваются программисты 1С при разработке выгрузок.

1. Неправильный порядок записи атрибутов и текста

Стандарты XML жестко регламентируют порядок следования метаданных внутри тега. Атрибуты узла всегда должны быть записаны сразу после открытия элемента, до того, как в поток будет направлено его текстовое содержимое или вложенные структуры.

Ошибочный вариант:


ЗаписьXML.ЗаписатьНачалоЭлемента("item");
ЗаписьXML.ЗаписатьТекст("Мобильный телефон"); // Текст пишется до атрибута
ЗаписьXML.ЗаписатьАтрибут("code", "0000045"); // Здесь возникнет ошибка!
ЗаписьXML.ЗаписатьКонецЭлемента();

Правильный вариант:


ЗаписьXML.ЗаписатьНачалоЭлемента("item");
ЗаписьXML.ЗаписатьАтрибут("code", "0000045"); // Сначала указываем все атрибуты
ЗаписьXML.ЗаписатьТекст("Мобильный телефон"); // Только после этого пишем текстовое содержимое
ЗаписьXML.ЗаписатьКонецЭлемента();

2. Попытка создания нескольких корневых элементов

Если в процессе формирования одного XML-документа вы полностью закрыли самый верхний элемент (корень), а затем снова вызвали метод ЗаписатьНачалоЭлемента(), платформа моментально заблокирует операцию.

Помните золотое правило разметки: один XML-документ — один корневой элемент. Если вам требуется выгрузить несколько равноправных списков данных (например, список контрагентов и список договоров), их необходимо обернуть в один общий родительский контейнер, например, <data> или <export>.

3. Неверное местоположение метода ЗаписатьОбъявлениеXML()

Метод ЗаписатьОбъявлениеXML() генерирует заголовок XML-документа, описывающий его версию и кодировку (например, <?xml version="1.0" encoding="UTF-8"?>). Данный метод должен выполняться самым первым в коде формирования документа.

Если вы попытаетесь вызвать его после открытия любого элемента или записи служебных комментариев, 1С вернет ошибку некорректного порядка записи, так как заголовок не может располагаться внутри тела XML-документа.

4. Конфликты при ручной записи и использовании механизмов XDTO

Очень часто разработчики комбинируют низкоуровневый вывод данных через ЗаписьXML и автоматическую сериализацию сложных объектов встроенными средствами платформы (например, через СериализаторXDTO или метод ФабрикаXDTO.ЗаписатьXML()).

В таких ситуациях ошибка порядка возникает, если:

5. Ошибки при объявлении пространств имен (Namespaces)

При интеграции со сложными корпоративными веб-сервисами или системами маркировки (например, «Честный Знак», МДЛП) активно используются пространства имен. Для этого применяется метод ЗаписатьСоответствиеПространстваИмен().

Вызывать его необходимо строго сразу после открытия того тега, для которого данное пространство объявляется. Попытка сопоставить пространство имен после того, как были записаны дочерние элементы или текстовые блоки, гарантированно приведет к аварийной остановке процесса.

Методические рекомендации по предотвращению ошибок

Для того чтобы сделать ваш код выгрузки надежным и простым в сопровождении, мы рекомендуем придерживаться нескольких правил:

Мы подробно разобрали ключевые причины возникновения ошибки «Ошибочный порядок записи XML» в 1С и изучили практические способы ее решения. Контролируйте последовательность открытия и закрытия тегов, соблюдайте структуру подчиненности элементов, и ваши механизмы интеграции будут работать стабильно и эффективно!

← На главную