ЗаписьXML, DOM и XDTO: формируем XML из кода 1С без ошибок

· 9 мин

Содержание

Программно создать 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 попадает пользовательский текст, начинаются проблемы:

  • 🔸 наименование «ООО Рога & Копыта» даёт невалидный файл, потому что & нужно записать как &amp;;
  • 🔸 кавычка внутри значения атрибута рвёт атрибут;
  • 🔸 конкатенация в цикле на десятках тысяч строк заметно замедляется: каждая операция создаёт новую строку;
  • 🔸 забытый закрывающий тег обнаружится только у получателя.

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

Запись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>

Символ & в комментарии объект запишет как &amp; сам. Это главное преимущество перед склейкой строк.

Три правила, которые держат структуру в порядке:

  1. Атрибуты пишутся сразу после ЗаписатьНачалоЭлемента(), до текста и дочерних элементов.
  2. Каждому ЗаписатьНачалоЭлемента() соответствует свой ЗаписатьКонецЭлемента().
  3. Значения переводите в строку функцией XMLСтрока(), а не Строка().

Шаг 4. Закрыть запись

Запись.ЗаписатьКонецЭлемента(); // </Orders>
Запись.Закрыть();

Без Закрыть() файл остаётся занятым процессом и может оказаться недописанным. Если в процессе возможны исключения, оборачивайте запись в Попытка … Исключение, и в обеих ветках закрывайте объект.

Почему XMLСтрока, а не Строка

ЗначениеСтрока()XMLСтрока()
Число 1234.51 234,5 (с неразрывным пробелом)1234.5
Дата 07.10.2026 14:3007.10.2026 14:30:002026-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-формате, без пробелов и запятых;
  • ✅ мусорные управляющие символы из наименований заменяются, и файл остаётся валидным;
  • ✅ вспомогательная процедура ЗаписатьПоле убирает повторяющиеся тройки вызовов.

Как отдать файл пользователю

Код выше работает на сервере, поэтому и файл появится на сервере. Чтобы пользователь получил его у себя:

  1. На сервере получите путь через ПолучитьИмяВременногоФайла("xml") и вызовите процедуру.
  2. Прочитайте файл в Новый ДвоичныеДанные(ИмяФайла) и поместите в хранилище: ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор).
  3. Удалите временный файл на сервере через УдалитьФайлы().
  4. На клиенте передайте адрес хранилища в метод получения файла с сервера (в современных конфигурациях — асинхронный НачатьПолучениеФайлаССервера).

Если файл нужен в общей папке, а не у пользователя, проверьте, что у учётной записи, от которой запущен сервер 1С, есть права на запись в эту папку.

Ошибки при создании XML-файла и как их найти

Диагностика: выберите симптом

Частые вопросы

Как проверить, что созданный XML соответствует XSD-схеме?
Загрузите схему как пакет XDTO и прочитайте файл через ФабрикаXDTO.ПрочитатьXML() с указанием типа: несоответствие даст исключение с описанием. Для ручной проверки подойдёт любой внешний валидатор XML по XSD.
Можно ли дописать элементы в уже существующий XML-файл?
ЗаписьXML дописывать не умеет. Прочитайте файл в ДокументDOM через ПостроительDOM, добавьте узлы и запишите заново через ЗаписьDOM.
Как записать CDATA?
У ЗаписьXML есть метод ЗаписатьСекциюCDATA(). Он нужен, когда получатель ждёт в элементе HTML или другой текст с разметкой без экранирования.
Чем XML из «Выгрузки/загрузки данных XML» отличается от своего формата?
Штатная обработка пишет данные во внутреннем формате сериализации 1С. Его читает только другая база 1С с такой же структурой. Для внешней системы нужен свой формат по её описанию.
Как вставить готовый фрагмент XML без экранирования?
Метод ЗаписатьБезОбработки() пишет строку как есть. Используйте его только для проверенных фрагментов: за валидность отвечаете вы.