Работа с XML-файлами в 1С — это стандартная задача при интеграции систем или выгрузке отчетности. Однако использование объекта ЗаписьXML требует строгого соблюдения последовательности вызова методов. Ошибка «Ошибочный порядок записи XML» является одной из самых распространенных и часто ставит разработчиков в тупик, так как она возникает при попытке закрыть тег, который не был открыт, или при записи атрибута в неподходящий момент. Рассмотрим подробно, как избежать этой проблемы и правильно структурировать код.
Проанализируем ситуацию, представленную в исходном коде. Автор пытается выгрузить табличную часть документа в XML, создавая для каждой строки элемент СведТов. Ошибка возникала из-за того, что в цикле или сразу после него вызывалось лишнее количество методов ЗаписатьКонецЭлемента(). В 1С объект ЗаписьXML работает по принципу стека: каждый вызов «начала элемента» должен иметь ровно один соответствующий вызов «конца элемента».
Рассмотрим исправленный алгоритм работы внутри цикла:
// Перед циклом обязательно открываем корневой элемент
Файл.ЗаписатьНачалоЭлемента("ТаблСчФакт");
Для каждого СтрДанных Из ЭкземплярДанныхДляПечати.ТабличнаяЧасть Цикл
НомерСтроки = НомерСтроки + 1;
// Открываем тег строки товара
Файл.ЗаписатьНачалоЭлемента("СведТов");
// Записываем атрибуты СРАЗУ после открытия элемента
Файл.ЗаписатьАтрибут("НомСтр", Строка(НомерСтроки));
Файл.ЗаписатьАтрибут("НаимТов", Строка(СтрДанных.Товар));
Файл.ЗаписатьАтрибут("ОКЕИ_Тов", Строка(СтрДанных.ЕдиницаИзмеренияКод));
Файл.ЗаписатьАтрибут("КолТов", Формат(СтрДанных.Количество, "ЧГ=0; ЧРД=."));
// Закрываем тег строки товара (только один раз!)
Файл.ЗаписатьКонецЭлемента();
КонецЦикла;
// Закрываем корневой элемент после завершения цикла
Файл.ЗаписатьКонецЭлемента();
Важный момент: если вы вызываете ЗаписатьКонецЭлемента() больше раз, чем было вызвано ЗаписатьНачалоЭлемента(), платформа выбросит исключение, так как ей «нечего закрывать».
Для того чтобы ваш XML-файл всегда был валидным, а код не вызывал ошибок, необходимо придерживаться строгой последовательности вызова методов. Разберем по шагам правила записи:
ЗаписатьОбъявлениеXML() должен вызываться первым, сразу после открытия файла. Это добавит в начало файла строку <?xml version="1.0" encoding="UTF-8"?>.ЗаписатьНачалоЭлемента() для корня, структура будет нарушена.ЗаписатьАтрибут() можно вызывать только непосредственно после ЗаписатьНачалоЭлемента(). Если вы уже записали вложенный элемент или текст с помощью ЗаписатьТекст(), добавить атрибут к текущему узлу уже не получится.В примере автора используется функция Строка() и Формат() для чисел. Проанализируем ситуацию: стандартное преобразование числа в строку в 1С зависит от региональных настроек сервера или клиента. Если в настройках разделителем дробной части стоит запятая, а разделителем групп разрядов — неразрывный пробел, то XML получится невалидным (например, 1 000,50 вместо 1000.50).
Для записи числовых данных в XML всегда используйте форматную строку, которая исключает разделители групп и устанавливает точку в качестве разделителя дробной части:
Формат(Сумма, "ЧГ=0; ЧРД=.")
Где ЧГ=0 убирает группировку разрядов, а ЧРД=. принудительно ставит точку.
Если вам нужно выгрузить объект целиком и вы не хотите вручную прописывать каждый тег, рассмотрим использование механизма XDTO. Это гораздо более надежный способ, который берет на себя контроль за порядком записи тегов. Выясним, как это работает на примере:
Процедура ВыгрузитьЧерезXDTO(ОбъектДляВыгрузки, ПутьКФайлу)
Запись = Новый ЗаписьXML;
// Устанавливаем параметры записи для красивого форматирования (отступов)
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина);
Запись.ОткрытьФайл(ПутьКФайлу, ПараметрыЗаписи);
Запись.ЗаписатьОбъявлениеXML();
// Используем СериализаторXDTO для автоматической записи
СериализаторXDTO.ЗаписатьXML(Запись, ОбъектДляВыгрузки);
Запись.Закрыть();
КонецПроцедуры
Преимущество этого метода: вы полностью застрахованы от ошибки «Ошибочный порядок записи XML», так как платформа сама формирует структуру. Недостаток: XDTO выгружает все реквизиты объекта. Если вам нужна специфическая структура (например, для ФНС), придется либо создавать свою XSD-схему, либо использовать ручную запись через ЗаписьXML, как мы разбирали выше.
Еще один способ избежать ошибок порядка — это сначала построить дерево документа в памяти, а затем записать его целиком. Посмотрим, как это выглядит с использованием ДокументDOM:
ДокDOM = Новый ДокументDOM;
КорневойУзел = ДокDOM.СоздатьЭлемент("Данные");
ДокDOM.ДобавитьПодчиненный(КорневойУзел);
Для каждого Стр Из ТаблицаДанных Цикл
УзелТовар = ДокDOM.СоздатьЭлемент("Товар");
УзелТовар.УстановитьАтрибут("Название", Стр.Наименование);
// Добавляем узел в дерево
КорневойУзел.ДобавитьПодчиненный(УзелТовар);
КонецЦикла;
// Теперь записываем всё дерево в файл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлу);
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокDOM, ЗаписьXML);
ЗаписьXML.Закрыть();
Этот подход позволяет добавлять атрибуты и элементы в любом порядке внутри дерева объектов, и только в момент вызова Записать() 1С сформирует финальный текстовый файл. Это исключает логические ошибки ручного управления потоком записи.
Для исправления ошибки «Ошибочный порядок записи XML» выполните следующие действия:
ЗаписатьНачалоЭлемента и ЗаписатьКонецЭлемента — они должны строго совпадать.ЗаписатьАтрибут идут сразу после открытия тега.