ЗаписьXML, DOM и XDTO: формируем XML из кода 1С без ошибок
Содержание
Программно создать XML-файл в 1С проще всего объектом ЗаписьXML: открыть файл методом ОткрытьФайл(), записать объявление, затем элементы, атрибуты и текст, и закрыть файл методом Закрыть(). Этот способ работает потоково, быстро и сам экранирует спецсимволы. Для правки готового дерева берут ДокументDOM, для выгрузки строго по XSD-схеме — XDTO, а склейку строк оставляют только для крошечных одноразовых фрагментов.
Всё ниже относится к платформе 1С:Предприятие 8.3 и не зависит от конфигурации: код одинаково работает в Бухгалтерии, УТ, ERP и самописных базах. От конфигурации зависят только имена документов и реквизитов в примере.
Способы создать XML в 1С: какой выбрать
В платформе четыре рабочих подхода. Они различаются тем, держат ли весь документ в памяти и насколько строго контролируют структуру.
| Способ | Когда выбирать | Ограничения и риски |
|---|---|---|
| Склейка строк | Фрагмент в 3–5 тегов для теста или лога | Нет экранирования &, <, >, легко сломать вложенность |
ЗаписьXML | Почти любая выгрузка, особенно большая | Пишет только вперёд: вернуться и поправить элемент нельзя |
ДокументDOM + ЗаписьDOM | Нужно собрать дерево, потом что-то вставить или удалить | Весь документ в памяти, на больших объёмах тяжело |
| XDTO (фабрика, пакет по XSD) | Формат задан схемой: обмен с госсервисом, банком, партнёром | Нужна схема, порог входа выше |
💡 Если сомневаетесь, начинайте с ЗаписьXML. На неё же опираются и DOM, и XDTO: в конце они тоже записывают результат через объект ЗаписьXML.
Сборка XML строкой: когда это оправдано
Ручная склейка выглядит так:
Текст = "<?xml version=""1.0"" encoding=""UTF-8""?>" + Символы.ПС
+ "<Ping time=""" + XMLСтрока(ТекущаяДатаСеанса()) + """/>";
Она допустима, когда все значения вы контролируете сами: даты, числа, фиксированные коды. Как только в XML попадает пользовательский текст, начинаются проблемы:
- 🔸 наименование «ООО Рога & Копыта» даёт невалидный файл, потому что
&нужно записать как&; - 🔸 кавычка внутри значения атрибута рвёт атрибут;
- 🔸 конкатенация в цикле на десятках тысяч строк заметно замедляется: каждая операция создаёт новую строку;
- 🔸 забытый закрывающий тег обнаружится только у получателя.
Вывод простой: строкой можно собрать шаблон для отладки, но не рабочую выгрузку.
ЗаписьXML: пошаговое создание XML-файла
Объект ЗаписьXML доступен на сервере и в толстом клиенте. Код выгрузки лучше держать в серверном общем модуле или в процедуре с директивой &НаСервере.
Шаг 1. Открыть файл или строку
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяФайла, "UTF-8");
Вторым параметром передают кодировку. Если получатель требует windows-1251, укажите её здесь, и объявление XML получит ту же кодировку.
Когда файл не нужен, а нужен текст (например, для отправки через HTTP), вместо ОткрытьФайл() вызовите Запись.УстановитьСтроку(). Тогда Запись.Закрыть() вернёт готовую строку.
Для красивых отступов используют объект ПараметрыЗаписиXML:
Параметры = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина);
Запись.ОткрытьФайл(ИмяФайла, Параметры);
Третий параметр Истина включает отступы. Для машинного обмена их можно выключить: файл станет меньше.
Шаг 2. Записать объявление и корневой элемент
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("Orders");
Запись.ЗаписатьАтрибут("version", "1.0");
Корневой элемент в XML должен быть ровно один. Всё остальное вкладывается в него.
С именами тегов есть свои ограничения. Имя не может начинаться с цифры и содержать пробелы. Кириллица в именах стандартом разрешена, но многие внешние системы её не принимают, поэтому для обмена надёжнее латиница.
Шаг 3. Вложенные элементы, атрибуты и текст
Запись.ЗаписатьНачалоЭлемента("Order");
Запись.ЗаписатьАтрибут("id", "ЗК-000125");
Запись.ЗаписатьНачалоЭлемента("Comment");
Запись.ЗаписатьТекст("Доставка до 18:00 & звонок заранее");
Запись.ЗаписатьКонецЭлемента(); // </Comment>
Запись.ЗаписатьКонецЭлемента(); // </Order>
Символ & в комментарии объект запишет как & сам. Это главное преимущество перед склейкой строк.
Три правила, которые держат структуру в порядке:
- Атрибуты пишутся сразу после
ЗаписатьНачалоЭлемента(), до текста и дочерних элементов. - Каждому
ЗаписатьНачалоЭлемента()соответствует свойЗаписатьКонецЭлемента(). - Значения переводите в строку функцией
XMLСтрока(), а неСтрока().
Шаг 4. Закрыть запись
Запись.ЗаписатьКонецЭлемента(); // </Orders>
Запись.Закрыть();
Без Закрыть() файл остаётся занятым процессом и может оказаться недописанным. Если в процессе возможны исключения, оборачивайте запись в Попытка … Исключение, и в обеих ветках закрывайте объект.
Почему XMLСтрока, а не Строка
| Значение | Строка() | XMLСтрока() |
|---|---|---|
| Число 1234.5 | 1 234,5 (с неразрывным пробелом) | 1234.5 |
| Дата 07.10.2026 14:30 | 07.10.2026 14:30:00 | 2026-10-07T14:30:00 |
| Булево Истина | Да | true |
Внешние системы ждут именно XML-представление. Если формат партнёра требует дату вида 07.10.2026, используйте Формат(Дата, "ДФ=dd.MM.yyyy"), а для чисел без разделителей групп — Формат(Число, "ЧРД=.; ЧГ=0").
Пространства имён, вложенные структуры и XDTO
Префиксы и xmlns
Многие форматы требуют пространства имён: xmlns, префиксы, ссылку на схему. Соответствие префикса и URI объявляют сразу после начала элемента:
URI = "urn:example:orders";
Запись.ЗаписатьНачалоЭлемента("Orders", URI);
Запись.ЗаписатьСоответствиеПространстваИмен("ord", URI);
Запись.ЗаписатьСоответствиеПространстваИмен("xsi", "http://www.w3.org/2001/XMLSchema-instance");
Запись.ЗаписатьАтрибут("schemaLocation", "http://www.w3.org/2001/XMLSchema-instance", URI + " orders.xsd");
Запись.ЗаписатьНачалоЭлемента("Order", URI); // запишется как ord:Order
Дальше передавайте URI вторым параметром, и платформа сама подставит объявленный префикс. Пространство имён по умолчанию (без префикса) объявляется пустой строкой вместо префикса.
💡 Объявляйте все нужные пространства имён на корневом элементе. Иначе платформа объявит их заново на каждом вложенном элементе, и файл распухнет.
ДокументDOM: когда нужно править дерево
DOM удобен, если структура известна не сразу: например, итоговую сумму нужно вписать в начало документа, а посчитать её можно только в конце.
Док = Новый ДокументDOM("", "Orders");
Корень = Док.ЭлементДокумента;
Заказ = Док.СоздатьЭлемент("Order");
Заказ.УстановитьАтрибут("id", "ЗК-000125");
Корень.ДобавитьДочерний(Заказ);
Сумма = Док.СоздатьЭлемент("Total");
Сумма.ТекстовоеСодержимое = XMLСтрока(15400.50);
Заказ.ДобавитьДочерний(Сумма);
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяФайла, "UTF-8");
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(Док, Запись);
Запись.Закрыть();
XDTO: выгрузка по XSD-схеме
Если у получателя есть XSD, надёжнее всего загрузить её в конфигуратор как пакет XDTO (ветка Общие → XDTO-пакеты). Тогда платформа знает типы, обязательные элементы и порядок полей:
URI = "urn:example:orders";
ТипЗаказ = ФабрикаXDTO.Тип(URI, "Order");
Заказ = ФабрикаXDTO.Создать(ТипЗаказ);
Заказ.Number = "ЗК-000125";
Заказ.Проверить(); // исключение, если объект не соответствует схеме
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяФайла, "UTF-8");
ФабрикаXDTO.ЗаписатьXML(Запись, Заказ);
Запись.Закрыть();
Метод Проверить() ловит пропущенные обязательные поля ещё до отправки. Для выгрузки самих объектов 1С во внутреннем формате есть СериализаторXDTO.ЗаписатьXML(Запись, Объект), но такой XML поймёт только другая база 1С.
Большие XML-файлы: скорость и память
На выгрузках в сотни тысяч строк выигрывает тот, кто не держит всё в памяти и не дёргает базу в цикле.
- ⚡ Пишите потоком.
ЗаписьXMLсбрасывает данные в файл по мере записи.ДокументDOMна таком объёме съедает память сервера. - ⚡ Получайте данные одним запросом. Обращение через точку (
Строка.Номенклатура.Артикул) в цикле читает объект из базы на каждой итерации. Нужные поля выбирайте в запросе. - ⚡ Обходите выборку, а не таблицу.
Результат.Выбрать()не грузит весь результат в память, в отличие отВыгрузить(). - ⚡ Отключайте отступы. Для машинного обмена они не нужны, а на больших файлах заметно добавляют объём.
- ⚡ Сжимайте результат. Объект
ЗаписьZipФайлаупакует XML перед передачей, текст сжимается хорошо. - ⚡ Делите выгрузку. Если формат позволяет, режьте файл на части по периоду или по числу записей, чтобы получатель не упирался в свои лимиты.
Длинную выгрузку на сервере запускайте фоновым заданием. Тогда пользователь не ждёт с зависшим окном, а сеанс не обрывается по таймауту.
Пример: выгрузка заказа покупателя в XML-файл
Пример ниже написан для документа ЗаказКлиента из УТ 11 и ERP. В Бухгалтерии похожую роль играет СчетНаОплатуПокупателю. Имена документа, табличной части и реквизитов сверьте в конфигураторе со своей базой.
Процедуру разместите в серверном общем модуле:
Процедура ВыгрузитьЗаказВXML(Ссылка, ИмяФайла) Экспорт
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.Текст =
"ВЫБРАТЬ
| Заказ.Номер КАК Номер,
| Заказ.Дата КАК Дата,
| ЕСТЬNULL(Заказ.Контрагент.ИНН, """") КАК ИНН,
| ПРЕДСТАВЛЕНИЕ(Заказ.Контрагент) КАК Покупатель,
| Заказ.СуммаДокумента КАК Сумма
|ИЗ
| Документ.ЗаказКлиента КАК Заказ
|ГДЕ
| Заказ.Ссылка = &Ссылка
|;
|ВЫБРАТЬ
| Т.НомерСтроки КАК НомерСтроки,
| ЕСТЬNULL(Т.Номенклатура.Артикул, """") КАК Артикул,
| ПРЕДСТАВЛЕНИЕ(Т.Номенклатура) КАК Наименование,
| Т.Количество КАК Количество,
| Т.Цена КАК Цена,
| Т.Сумма КАК Сумма
|ИЗ
| Документ.ЗаказКлиента.Товары КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка
|УПОРЯДОЧИТЬ ПО
| НомерСтроки";
Результаты = Запрос.ВыполнитьПакет();
Шапка = Результаты[0].Выбрать();
Если НЕ Шапка.Следующий() Тогда
ВызватьИсключение "Заказ не найден";
КонецЕсли;
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяФайла, Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина));
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("Order");
Запись.ЗаписатьАтрибут("number", СокрЛП(Шапка.Номер));
Запись.ЗаписатьАтрибут("date", Формат(Шапка.Дата, "ДФ=yyyy-MM-dd"));
Запись.ЗаписатьНачалоЭлемента("Buyer");
Запись.ЗаписатьАтрибут("inn", Шапка.ИНН);
Запись.ЗаписатьТекст(Шапка.Покупатель);
Запись.ЗаписатьКонецЭлемента();
Запись.ЗаписатьНачалоЭлемента("Items");
Строки = Результаты[1].Выбрать();
Пока Строки.Следующий() Цикл
Запись.ЗаписатьНачалоЭлемента("Item");
Запись.ЗаписатьАтрибут("line", XMLСтрока(Строки.НомерСтроки));
ЗаписатьПоле(Запись, "Sku", Строки.Артикул);
ЗаписатьПоле(Запись, "Name", Строки.Наименование);
ЗаписатьПоле(Запись, "Qty", Строки.Количество);
ЗаписатьПоле(Запись, "Price", Строки.Цена);
ЗаписатьПоле(Запись, "Amount", Строки.Сумма);
Запись.ЗаписатьКонецЭлемента(); // </Item>
КонецЦикла;
Запись.ЗаписатьКонецЭлемента(); // </Items>
ЗаписатьПоле(Запись, "Total", Шапка.Сумма);
Запись.ЗаписатьКонецЭлемента(); // </Order>
Запись.Закрыть();
КонецПроцедуры
Процедура ЗаписатьПоле(Запись, Имя, Значение)
Запись.ЗаписатьНачалоЭлемента(Имя);
Запись.ЗаписатьТекст(ЗаменитьНедопустимыеСимволыXML(XMLСтрока(Значение)));
Запись.ЗаписатьКонецЭлемента();
КонецПроцедуры
Что даёт такая схема:
- ✅ данные берутся одним пакетным запросом, без обращений через точку в цикле;
- ✅ числа и даты выходят в XML-формате, без пробелов и запятых;
- ✅ мусорные управляющие символы из наименований заменяются, и файл остаётся валидным;
- ✅ вспомогательная процедура
ЗаписатьПолеубирает повторяющиеся тройки вызовов.
Как отдать файл пользователю
Код выше работает на сервере, поэтому и файл появится на сервере. Чтобы пользователь получил его у себя:
- На сервере получите путь через
ПолучитьИмяВременногоФайла("xml")и вызовите процедуру. - Прочитайте файл в
Новый ДвоичныеДанные(ИмяФайла)и поместите в хранилище:ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор). - Удалите временный файл на сервере через
УдалитьФайлы(). - На клиенте передайте адрес хранилища в метод получения файла с сервера (в современных конфигурациях — асинхронный
НачатьПолучениеФайлаССервера).
Если файл нужен в общей папке, а не у пользователя, проверьте, что у учётной записи, от которой запущен сервер 1С, есть права на запись в эту папку.
Ошибки при создании XML-файла и как их найти
ЗаменитьНедопустимыеСимволыXML(), а для поиска проблемной строки — через НайтиНедопустимыеСимволыXML().ЗаписатьАтрибут() сразу за ЗаписатьНачалоЭлемента().ЗаписатьКонецЭлемента() или пропущенный. Подпишите каждый конец комментарием с именем тега и сверьте пары.ОткрытьФайл() не совпадает с той, которую ждёт получатель, или файл потом пересохранили другим редактором. Укажите нужную кодировку явно и откройте результат редактором, который показывает кодировку.Строка() или вставлено в текст напрямую. Используйте XMLСтрока() или Формат() с явными параметрами.Закрыть(), обычно из-за исключения посередине выгрузки. Закрывайте запись и в ветке Исключение.Частые вопросы
Как проверить, что созданный XML соответствует XSD-схеме?
ФабрикаXDTO.ПрочитатьXML() с указанием типа: несоответствие даст исключение с описанием. Для ручной проверки подойдёт любой внешний валидатор XML по XSD.Можно ли дописать элементы в уже существующий XML-файл?
ЗаписьXML дописывать не умеет. Прочитайте файл в ДокументDOM через ПостроительDOM, добавьте узлы и запишите заново через ЗаписьDOM.Как записать CDATA?
ЗаписьXML есть метод ЗаписатьСекциюCDATA(). Он нужен, когда получатель ждёт в элементе HTML или другой текст с разметкой без экранирования.Чем XML из «Выгрузки/загрузки данных XML» отличается от своего формата?
Как вставить готовый фрагмент XML без экранирования?
ЗаписатьБезОбработки() пишет строку как есть. Используйте его только для проверенных фрагментов: за валидность отвечаете вы.