XML в 1С программно: запись, чтение, DOM и выгрузка данных
XML по-прежнему остаётся одним из главных форматов обмена: на нём построены обмены через EnterpriseData, электронные документы, выгрузки для банков и государственных сервисов. В статье — программная работа с XML в 1С 8.3: как сформировать документ через ЗаписьXML, как прочитать XML потоково и через DOM, как получить из XML структуру, как выгрузить справочник и что означает «Ошибка разбора XML».
Когда XML нужно обрабатывать программно
Слово «XML в 1С» означает для разных людей разное. Бухгалтеру обычно нужно загрузить готовый файл обмена — для этого в типовых конфигурациях есть встроенные обработки и механизмы синхронизации, и вопрос «как загрузить XML в 1С» решается через меню, без кода. Программист же сталкивается с XML формат в 1С, когда нужно сформировать файл по чужой схеме, разобрать ответ веб-сервиса или сделать нестандартную 1С выгрузку в XML. Об этом и пойдёт речь.
В платформе есть три подхода. ЗаписьXML и ЧтениеXML работают потоково — быстро и экономно. ПостроительDOM и ЗаписьDOM строят и сохраняют полное дерево документа. СериализаторXDTO и фабрика XDTO работают со схемами XSD и объектами конфигурации. Начнём с первого.
Запись XML: ЗаписьXML
Документ формируется последовательными вызовами: объявление, открытие элемента, атрибуты, текст, закрытие элемента. Каждому ЗаписатьНачалоЭлемента должен соответствовать свой ЗаписатьКонецЭлемента. Атрибуты записываются сразу после начала элемента, до любого вложенного содержимого.
Запись = Новый ЗаписьXML;
Параметры = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина); // кодировка, версия, отступы
Запись.ОткрытьФайл("C:\Exchange\catalog.xml", Параметры);
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("catalog");
Запись.ЗаписатьАтрибут("date", XMLСтрока(ТекущаяДатаСеанса()));
Запись.ЗаписатьНачалоЭлемента("item");
Запись.ЗаписатьАтрибут("id", "1");
Запись.ЗаписатьНачалоЭлемента("name");
Запись.ЗаписатьТекст("Болт М8 & гайка"); // & будет экранирован автоматически
Запись.ЗаписатьКонецЭлемента();
Запись.ЗаписатьНачалоЭлемента("price");
Запись.ЗаписатьТекст(XMLСтрока(12.5));
Запись.ЗаписатьКонецЭлемента();
Запись.ЗаписатьКонецЭлемента(); // item
Запись.ЗаписатьКонецЭлемента(); // catalog
Запись.Закрыть();
Числа и даты записывайте через XMLСтрока: она даёт формат, не зависящий от региональных настроек, — точка в качестве разделителя дробной части и дата вида 2026-10-31T14:05:00. Если записать число через Строка(), можно получить неразрывные пробелы между разрядами и запятую, и принимающая сторона не разберёт значение.
Строка XML 1С: запись в память
Чтобы получить строку XML для отправки в веб-сервис, вместо ОткрытьФайл вызовите УстановитьСтроку. Результат возвращает метод Закрыть.
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("request");
Запись.ЗаписатьАтрибут("method", "getStatus");
Запись.ЗаписатьТекст("A-1024");
Запись.ЗаписатьКонецЭлемента();
ТекстXML = Запись.Закрыть();
// <?xml version="1.0" encoding="UTF-8"?><request method="getStatus">A-1024</request>
Чтение XML потоком: ЧтениеXML
Потоковое чтение — основной способ, когда нужно 1С прочитать XML большого размера. Объект проходит по документу узел за узлом в цикле Пока Чтение.Прочитать(). На каждом шаге свойство ТипУзла сообщает, что перед нами: начало элемента, текст или конец элемента; Имя — имя элемента; Значение — текст; метод ЗначениеАтрибута возвращает атрибут текущего элемента.
Функция ПрочитатьКаталог(ПутьКФайлу)
Товары = Новый Массив;
Текущий = Неопределено;
ТекущееПоле = "";
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ПутьКФайлу);
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если Чтение.Имя = "item" Тогда
Текущий = Новый Структура("id, name, price", Чтение.ЗначениеАтрибута("id"), "", 0);
ИначеЕсли Текущий <> Неопределено Тогда
ТекущееПоле = Чтение.Имя;
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст И Текущий <> Неопределено Тогда
Если ТекущееПоле = "name" Тогда
Текущий.name = Чтение.Значение;
ИначеЕсли ТекущееПоле = "price" Тогда
Текущий.price = XMLЗначение(Тип("Число"), Чтение.Значение);
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
Если Чтение.Имя = "item" Тогда
Товары.Добавить(Текущий);
Текущий = Неопределено;
КонецЕсли;
ТекущееПоле = "";
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Возврат Товары;
КонецФункции
Обратная к XMLСтрока функция XMLЗначение превращает текст обратно в число, дату или булево. Если XML пришёл строкой, а не файлом, используйте Чтение.УстановитьСтроку(ТекстXML). Учтите, что при наличии пробелов и переносов между элементами парсер может выдавать узлы с пробельным текстом; в примере они безвредны, потому что текст присваивается только внутри известных полей.
DOM и XPath
Когда документ небольшой, а логика сложная — нужно искать узлы по условию, возвращаться назад, менять документ, — удобнее DOM. ПостроительDOM читает XML в объект ДокументDOM, по которому можно перемещаться и делать выборки XPath.
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Exchange\catalog.xml");
Построитель = Новый ПостроительDOM;
Документ = Построитель.Прочитать(Чтение);
Чтение.Закрыть();
// Все элементы item
Элементы = Документ.ПолучитьЭлементыПоИмени("item");
Для Каждого Элемент Из Элементы Цикл
Сообщить(Элемент.ПолучитьАтрибут("id"));
КонецЦикла;
// XPath: имя товара с id = 1
Разыменователь = Новый РазыменовательПространствИменDOM(Документ);
Результат = Документ.ВычислитьВыражениеXPath("/catalog/item[@id='1']/name", Документ,
Разыменователь, ТипРезультатаDOMXPath.УпорядоченныйИтераторУзлов);
Узел = Результат.ПолучитьСледующий();
Пока Узел <> Неопределено Цикл
Сообщить(Узел.ТекстовоеСодержимое);
Узел = Результат.ПолучитьСледующий();
КонецЦикла;
Если в документе объявлено пространство имён по умолчанию (атрибут xmlns у корня), простое выражение вида /catalog/item ничего не найдёт: узлы принадлежат пространству имён. В этом случае создают разыменователь с явным соответствием префикса и URI и используют префикс в выражении. Сохранить изменённый документ можно объектом ЗаписьDOM: создайте его и вызовите ЗаписьDOM.Записать(Документ, ЗаписьXML), передав подготовленный объект ЗаписьXML с открытым файлом или строкой.
1С XML в структуру: рекурсивная функция
Для небольших ответов сервисов удобно один раз превратить XML в коллекции 1С и дальше работать с ними как с JSON. Функция ниже обходит дерево DOM рекурсивно: элемент без дочерних элементов становится строкой, элемент с дочерними — соответствием, повторяющиеся имена собираются в массив. Атрибуты сохраняются с префиксом @. Используется Соответствие, а не Структура, потому что имена элементов могут содержать дефисы и двоеточия.
Функция УзелВЗначение(Узел)
Результат = Новый Соответствие;
ЕстьДочерниеЭлементы = Ложь;
Если Узел.Атрибуты <> Неопределено Тогда
Для Каждого Атрибут Из Узел.Атрибуты Цикл
Результат.Вставить("@" + Атрибут.ИмяУзла, Атрибут.Значение);
КонецЦикла;
КонецЕсли;
Для Каждого Дочерний Из Узел.ДочерниеУзлы Цикл
Если Дочерний.ТипУзла <> ТипУзлаDOM.Элемент Тогда
Продолжить;
КонецЕсли;
ЕстьДочерниеЭлементы = Истина;
Имя = Дочерний.ЛокальноеИмя;
Значение = УзелВЗначение(Дочерний);
Существующее = Результат.Получить(Имя);
Если Существующее = Неопределено Тогда
Результат.Вставить(Имя, Значение);
ИначеЕсли ТипЗнч(Существующее) = Тип("Массив") Тогда
Существующее.Добавить(Значение);
Иначе
Список = Новый Массив;
Список.Добавить(Существующее);
Список.Добавить(Значение);
Результат.Вставить(Имя, Список);
КонецЕсли;
КонецЦикла;
Если Не ЕстьДочерниеЭлементы И Результат.Количество() = 0 Тогда
Возврат Узел.ТекстовоеСодержимое;
ИначеЕсли Не ЕстьДочерниеЭлементы Тогда
Результат.Вставить("#text", Узел.ТекстовоеСодержимое);
КонецЕсли;
Возврат Результат;
КонецФункции
// Вызов
Данные = УзелВЗначение(Документ.ДокументЭлемент);
Ограничение подхода: если элемент встречается то один, то несколько раз, в одном файле вы получите строку или соответствие, а в другом — массив. Код, который потом работает с результатом, должен проверять тип значения.
Пример: выгрузка справочника в XML
Типовая задача — выгрузить номенклатуру для сайта или внешней системы. Получаем данные запросом и пишем потоком: это работает быстро даже на десятках тысяч элементов, потому что в памяти не держится весь документ. Уникальный идентификатор ссылки через XMLСтрока даёт стабильный ключ для сопоставления при повторной загрузке.
Процедура ВыгрузитьНоменклатуру(ПутьКФайлу) Экспорт
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления
| И НЕ Номенклатура.ЭтоГруппа");
Выборка = Запрос.Выполнить().Выбрать();
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ПутьКФайлу, Новый ПараметрыЗаписиXML("UTF-8", , Истина));
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("products");
Пока Выборка.Следующий() Цикл
Запись.ЗаписатьНачалоЭлемента("product");
Запись.ЗаписатьАтрибут("guid", XMLСтрока(Выборка.Ссылка));
Запись.ЗаписатьАтрибут("code", СокрЛП(Выборка.Код));
Запись.ЗаписатьНачалоЭлемента("name");
Запись.ЗаписатьТекст(Выборка.Наименование);
Запись.ЗаписатьКонецЭлемента();
Запись.ЗаписатьНачалоЭлемента("sku");
Запись.ЗаписатьТекст(Выборка.Артикул);
Запись.ЗаписатьКонецЭлемента();
Запись.ЗаписатьКонецЭлемента();
КонецЦикла;
Запись.ЗаписатьКонецЭлемента();
Запись.Закрыть();
КонецПроцедуры
Имена реквизитов (Артикул, признак групп) зависят от конфигурации — подставьте свои. Документы выгружаются так же, только запрос строится к шапке и табличной части, а строки табличной части пишутся вложенными элементами. Если обмен идёт между двумя базами 1С с одной конфигурацией, проще использовать СериализаторXDTO.ЗаписатьXML(Запись, Объект): он выгрузит объект целиком со всеми реквизитами и табличными частями.
Обратная задача: загрузка из XML в справочник
Загрузка строится на тех же приёмах. Файл читается функцией ПрочитатьКаталог из раздела о потоковом чтении или рекурсивной функцией через DOM, а затем для каждого элемента ищется существующая запись. Если в файле есть GUID, выгруженный из 1С, ссылку можно восстановить напрямую; если GUID нет — ищите по коду или артикулу.
Для Каждого Товар Из ПрочитатьКаталог(ПутьКФайлу) Цикл Ссылка = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(Товар.id)); Объект = Ссылка.ПолучитьОбъект(); Если Объект = Неопределено Тогда Объект = Справочники.Номенклатура.СоздатьЭлемент(); Объект.УстановитьСсылкуНового(Ссылка); КонецЕсли; Объект.Наименование = Товар.name; Объект.Записать(); КонецЦикла;
Пример предполагает, что в атрибуте id лежит GUID; если там обычный код, конструктор УникальныйИдентификатор вызовет исключение, поэтому для внешних файлов поиск по коду через НайтиПоКоду надёжнее. Массовую загрузку выполняйте в транзакции порциями и на сервере, а ошибки отдельных элементов записывайте в журнал, чтобы один некорректный элемент не останавливал весь обмен.
«Ошибка разбора XML»: причины и решение
Сообщение «Ошибка разбора XML» означает, что текст не соответствует синтаксису XML. Ошибка разбора XML 1С обычно сопровождается номером строки и позиции — начинайте поиск с этого места. Частые причины:
- Кодировка. В объявлении указано
encoding="windows-1251", а файл фактически в UTF-8, или наоборот. Кириллица превращается в недопустимые последовательности байтов. - Мусор перед объявлением. Объявление
<?xml ...?>должно быть самым началом документа. Пробел, перенос строки или лишний BOM, добавленный при склейке строк, ломают разбор. - Неэкранированные символы. Знаки
&и<в тексте должны записываться как&и<. Это происходит, когда XML собирают конкатенацией строк, а не черезЗаписьXML. - Обрезанный файл. Передача прервалась, и закрывающих тегов нет.
- Управляющие символы. Символы с кодами ниже 32 (кроме табуляции и переносов) недопустимы в XML 1.0 — их стоит удалить из текста перед записью.
- Не XML вообще. Веб-сервис вернул HTML-страницу ошибки или JSON.
Лучшая профилактика — формировать XML только через ЗаписьXML или DOM, никогда не склеивать теги вручную: объект сам экранирует спецсимволы и записывает корректное объявление.
Типичные ошибки
- Несбалансированные
ЗаписатьНачалоЭлементаиЗаписатьКонецЭлемента— исключение при закрытии или некорректный документ. - Вызов
ЗаписатьАтрибутпослеЗаписатьТекстили вложенного элемента — атрибуты пишутся только сразу после начала элемента. - Числа и даты через
Строка()вместоXMLСтрока. - DOM для файлов в сотни мегабайт — нехватка памяти на сервере; используйте
ЧтениеXML. - XPath без учёта пространства имён по умолчанию.
| Задача | Объект |
|---|---|
| Сформировать файл или строку | ЗаписьXML |
| Прочитать большой файл | ЧтениеXML |
| Поиск узлов, изменение документа | ПостроительDOM, ДокументDOM, XPath |
| Объект 1С целиком | СериализаторXDTO |
Актуальность для новых версий
Объекты ЗаписьXML, ЧтениеXML, ПостроительDOM и ЗаписьDOM существуют давно и в платформе 8.3 вплоть до актуальных релизов 8.3.2x работают одинаково, так что примеры не зависят от конкретного релиза. Для больших файлов предпочтительнее потоковое чтение: DOM загружает документ в память целиком. При обмене между базами 1С с одинаковыми метаданными удобнее СериализаторXDTO, а для обменов с типовыми конфигурациями — формат EnterpriseData.
Частые вопросы
Чем ЧтениеXML отличается от ПостроительDOM?
ЧтениеXML читает документ последовательно, узел за узлом, и почти не расходует память. ПостроительDOM загружает весь документ в дерево, по которому можно свободно перемещаться и искать узлы через XPath, но для файлов в сотни мегабайт это затратно.
Как получить XML в строку, а не в файл?
Вызовите у ЗаписьXML метод УстановитьСтроку вместо ОткрытьФайл. После записи метод Закрыть вернёт сформированный текст.
Почему возникает «Ошибка разбора XML»?
Чаще всего из-за несоответствия кодировки в объявлении и фактической, BOM или мусора перед объявлением, неэкранированных символов & и < в тексте или обрезанного файла. Проверьте первые символы файла и его конец.
Как загрузить XML в 1С бухгалтеру без программирования?
В типовых конфигурациях для этого есть встроенные механизмы обмена и загрузки, а также универсальная обработка «Выгрузка и загрузка данных XML». Программный код нужен, когда формат файла нестандартный.