Запись XML из 1С 8: код, кодировка, атрибуты и большие выгрузки

· 9 мин

Содержание

Записать XML файл в 1С 8.3 проще всего объектом ЗаписьXML: ОткрытьФайл() → ЗаписатьОбъявлениеXML() → пары ЗаписатьНачалоЭлемента() / ЗаписатьКонецЭлемента() → Закрыть(). Объект пишет поток последовательно, сам экранирует спецсимволы и не держит документ в памяти.

Частое заблуждение — собирать XML конкатенацией строк: "<Товар>" + Наименование + "</Товар>". Первый же контрагент с амперсандом в названии («Рога & Копыта») сломает файл, а получатель увидит ошибку разбора. ЗаписьXML превращает & в &amp; автоматически. Строки оставьте для логов, а файлы обмена пишите объектом.

Что решить до первой строки кода

Перед программированием ответьте себе на пять вопросов. От них зависит выбор метода и половина будущих ошибок.

  • 📄 Кто читает файл. Внешняя система с описанной схемой (XSD) — значит, имена элементов, порядок и пространства имён диктует она. Другая база 1С — можно сериализовать объекты целиком.
  • 🔤 Кодировка. По умолчанию ЗаписьXML пишет в UTF-8. Старые учётные программы и банковские клиенты нередко ждут windows-1251.
  • 🧭 Где выполняется код. Файл на диске создаётся там, где работает код. Серверный код пишет на диск сервера, а не на компьютер пользователя.
  • 📦 Объём. Сотня строк и полмиллиона строк требуют разной техники (раздел про производительность ниже).
  • 🧾 Форматы значений. Даты, числа и булево в XML пишут в каноническом виде, а не так, как их показывает форма.

Последний пункт стоит разобрать сразу. Функция Строка() форматирует значение по региональным настройкам: число 1234,5 превратится в 1 234,5 с неразрывным пробелом. Для XML нужна XMLСтрока():

Значение в 1ССтрока()XMLСтрока()
Число 1234,51 234,51234.5
Дата 07.10.2026 14:3007.10.2026 14:30:002026-10-07T14:30:00
Булево ИстинаДаtrue
Ссылка на справочникнаименованиеуникальный идентификатор (GUID)

Если получателю нужна дата без времени, используйте Формат(Дата, "ДФ=yyyy-MM-dd"). Для чисел с фиксированным числом знаков — Формат(Сумма, "ЧДЦ=2; ЧРД=.; ЧГ=0").

Пошаговая запись XML файла через ЗаписьXML

Задача: выгрузить список товаров с ценами в файл C:\Exchange\goods.xml. Структура результата:

<?xml version="1.0" encoding="UTF-8"?>
<Каталог Дата="2026-10-07T14:30:00">
  <Товар Код="00001">
    <Наименование>Бумага A4</Наименование>
    <Цена>450.00</Цена>
  </Товар>
</Каталог>

Шаг 1. Создать объект и открыть файл

ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина);
ЗаписьXML.ОткрытьФайл("C:\Exchange\goods.xml", ПараметрыЗаписи);

Третий параметр конструктора ПараметрыЗаписиXML включает отступы. Файл становится читаемым для человека, но растёт в размере. Для машинного обмена отступы можно выключить.

Шаг 2. Записать объявление и корневой элемент

ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Каталог");
ЗаписьXML.ЗаписатьАтрибут("Дата", XMLСтрока(ТекущаяДатаСеанса()));

Объявление <?xml ...?> пишется один раз и строго первым. Атрибуты элемента записываются сразу после ЗаписатьНачалоЭлемента(), до любого текста и вложенных элементов.

Шаг 3. Выгрузить данные в цикле

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Номенклатура.Код КАК Код,
|	Номенклатура.Наименование КАК Наименование
|ИЗ
|	Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|	НЕ Номенклатура.ЭтоГруппа";
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
	ЗаписьXML.ЗаписатьНачалоЭлемента("Товар");
	ЗаписьXML.ЗаписатьАтрибут("Код", СокрЛП(Выборка.Код));

	ЗаписьXML.ЗаписатьНачалоЭлемента("Наименование");
	ЗаписьXML.ЗаписатьТекст(Выборка.Наименование);
	ЗаписьXML.ЗаписатьКонецЭлемента();

	ЗаписьXML.ЗаписатьНачалоЭлемента("Цена");
	ЗаписьXML.ЗаписатьТекст(Формат(ПолучитьЦену(Выборка.Код), "ЧДЦ=2; ЧРД=.; ЧГ=0; ЧН=0.00"));
	ЗаписьXML.ЗаписатьКонецЭлемента();

	ЗаписьXML.ЗаписатьКонецЭлемента(); // Товар
КонецЦикла;

ПолучитьЦену() — ваша функция получения цены. В реальной выгрузке цену лучше взять тем же запросом через соединение с регистром цен, чтобы не делать запрос на каждую строку.

Шаг 4. Закрыть корень и файл

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

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

Каркас с защитой от незакрытого файла выглядит так:

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла, "UTF-8");
Попытка
	ЗаписьXML.ЗаписатьОбъявлениеXML();
	ЗаписьXML.ЗаписатьНачалоЭлемента("Каталог");
	// ... выгрузка данных ...
	ЗаписьXML.ЗаписатьКонецЭлемента();
	ЗаписьXML.Закрыть();
Исключение
	Попытка
		ЗаписьXML.Закрыть();
	Исключение
		// файл уже закрыт или повреждён
	КонецПопытки;
	УдалитьФайлы(ИмяФайла);
	ВызватьИсключение;
КонецПопытки;

Недописанный файл удаляется сразу. Так получатель не заберёт обрезанную выгрузку как готовую, а ошибка уйдёт в журнал регистрации с исходным текстом.

Запись в строку вместо файла

Для веб-сервиса или HTTP-запроса файл не нужен. Замените ОткрытьФайл() на УстановитьСтроку(), а результат заберите из Закрыть():

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8");
// ... запись элементов ...
ТекстXML = ЗаписьXML.Закрыть();

Пишем файл для пользователя, а не на сервер

В клиент-серверной базе код с ОткрытьФайл("C:\...") на сервере создаст файл на диске сервера. Пользователю нужен другой путь:

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

Атрибуты, пространства имён и префиксы

Внешние схемы почти всегда требуют пространство имён. Например, файл должен начинаться так: <doc:Документ xmlns:doc="urn:example:invoice">.

Соответствие префикса и URI объявляют сразу после открытия элемента:

URI = "urn:example:invoice";
ЗаписьXML.ЗаписатьНачалоЭлемента("Документ", URI);
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("doc", URI);
ЗаписьXML.ЗаписатьАтрибут("Версия", "2.0");

ЗаписьXML.ЗаписатьНачалоЭлемента("Номер", URI);
ЗаписьXML.ЗаписатьТекст("15");
ЗаписьXML.ЗаписатьКонецЭлемента();

Вложенный элемент с тем же URI получит префикс doc: автоматически: объект помнит объявленные соответствия внутри текущей ветки.

Пространство имён по умолчанию объявляют пустым префиксом: ЗаписатьСоответствиеПространстваИмен("", URI). Тогда элементы с этим URI пишутся без префикса.

Что ещё пригодится при работе с атрибутами:

  • 🔹 Атрибут с пространством имён: ЗаписатьАтрибут(Имя, URI, Значение) — три параметра вместо двух.
  • 🔹 Пустой элемент <Примечание/> получится, если между началом и концом элемента ничего не записать.
  • 🔹 ЗаписатьСекциюCDATA() — для текста, который получатель хочет видеть «как есть», например HTML-фрагмента.
  • 🔹 ЗаписатьКомментарий() — комментарий <!-- --> внутри файла. Удобно для отметки версии выгрузки.
  • 🔹 ЗаписатьБезОбработки() — вставляет строку без экранирования. Нужен, когда у вас уже есть готовый XML-фрагмент. Отвечаете за его корректность сами.

Префикс в имени элемента (ЗаписатьНачалоЭлемента("doc:Номер")) без объявленного xmlns:doc даст файл, который валидатор отклонит как ошибочный. Передавайте URI вторым параметром, а префикс пусть подставит платформа.

Сериализация объектов 1С в XML без ручной разметки

Когда файл читает другая база 1С с той же конфигурацией, размечать каждое поле вручную незачем. Платформа умеет записать объект целиком: документ с табличными частями, элемент справочника, набор записей регистра, константу.

Для этого есть СериализаторXDTO (свойство глобального контекста, доступно на сервере):

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Данные");
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("xsi", "http://www.w3.org/2001/XMLSchema-instance");

ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ДокументОбъект, НазначениеТипаXML.Явное);

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

НазначениеТипаXML.Явное добавляет атрибут xsi:type. Он нужен, чтобы при чтении платформа поняла, какой тип восстанавливать. Обратная операция — СериализаторXDTO.ПрочитатьXML(ЧтениеXML).

Три подхода к записи и когда какой брать:

СпособКогдаОграничения
ЗаписьXML вручнуюВнешняя система, своя схема, нужна полная власть над структуройБольше кода
СериализаторXDTOОбмен между базами 1С одной конфигурации, бэкап отдельных объектовФормат внутренний, внешняя система его не поймёт
Пакет XDTO + ФабрикаXDTO.ЗаписатьXML()Есть XSD от получателя, нужна строгая типизацияНадо импортировать схему в конфигурацию как пакет XDTO

Ссылки при сериализации пишутся как GUID. Если в базе-приёмнике нет объекта с таким идентификатором, после загрузки появится «битая» ссылка <Объект не найден>. Выгружайте связанные справочники вместе с документом или сопоставляйте их по коду.

Без программирования объекты выгружает обработка «Выгрузка и загрузка данных XML» с диска ИТС. Её используют для разового переноса между базами одной конфигурации. Для постоянного обмена она не годится.

Кодировка, проверка результата и разбор ошибок

Запись в windows-1251

ЗаписьXML.ОткрытьФайл(ИмяФайла, "windows-1251");
ЗаписьXML.ЗаписатьОбъявлениеXML();

Объявление само получит encoding="windows-1251", а текст сохранится в этой кодировке. Ошибка, которая встречается чаще всего, — записать файл в UTF-8, а в объявлении вручную через ЗаписатьБезОбработки() указать windows-1251. Получатель увидит «кракозябры».

Ещё один источник проблем — метка порядка байтов (BOM) в начале UTF-8 файла. Некоторые внешние парсеры на ней спотыкаются. У ОткрытьФайл() есть третий параметр, который отключает запись этой метки. Проверьте его в синтакс-помощнике своей версии платформы.

Недопустимые символы

XML 1.0 запрещает управляющие символы с кодами 1–31, кроме табуляции (9), перевода строки (10) и возврата каретки (13). Они попадают в наименования при копировании из Excel и PDF. Очистите строку перед записью:

Функция ОчиститьДляXML(Знач Текст)
	Для Код = 1 По 31 Цикл
		Если Код = 9 Или Код = 10 Или Код = 13 Тогда
			Продолжить;
		КонецЕсли;
		Текст = СтрЗаменить(Текст, Символ(Код), "");
	КонецЦикла;
	Возврат Текст;
КонецФункции

Проверка, что файл корректен

Самая дешёвая проверка — прочитать файл обратно. Построитель DOM упадёт с исключением на первом нарушении структуры:

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
Построитель = Новый ПостроительDOM;
Попытка
	ДокументDOM = Построитель.Прочитать(ЧтениеXML);
	Сообщить("Файл корректен");
Исключение
	Сообщить("Ошибка структуры: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
ЧтениеXML.Закрыть();

Так вы поймаете незакрытые элементы и неверную кодировку. Соответствие схеме получателя этим не проверить. Для этого импортируйте XSD в пакет XDTO и создавайте данные через фабрику: ошибка типа или недопустимый элемент проявятся при формировании объекта.

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

Большие выгрузки: память и скорость

ЗаписьXML пишет поток и сама по себе экономна. Память и время съедают обычно не запись, а то, что вокруг неё.

Что реально ускоряет выгрузку на сотнях тысяч строк:

  1. Выбрать() вместо Выгрузить(). Выборка тянет строки порциями, таблица значений целиком сидит в памяти.
  2. Один запрос вместо запроса в цикле. Цены, остатки и реквизиты связанных справочников соедините в исходном запросе. Запрос на каждую строку — главный тормоз ручных выгрузок.
  3. Без обращения к реквизитам через точку в цикле. Выборка.Номенклатура.Артикул делает скрытое чтение объекта. Выберите Номенклатура.Артикул полем запроса.
  4. Без ПолучитьОбъект() на каждую строку. Объект читает все табличные части. Если нужны три поля — берите их запросом.
  5. Отступы выключены. Для файла в несколько сотен мегабайт отступы добавляют заметный объём.
  6. Файл, а не строка. УстановитьСтроку() держит весь результат в памяти до Закрыть(). Для больших объёмов пишите сразу в файл.
  7. Без ЗаписьDOM и ДокументDOM для сборки. DOM строит дерево в памяти целиком. Он хорош для правки небольших файлов, но не для генерации гигабайтных выгрузок.

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

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

Частые вопросы о записи XML в 1С

Можно ли записать XML в тонком клиенте без обращения к серверу?
Да, ЗаписьXML доступна на клиенте. Но данные из базы всё равно придут с сервера, поэтому выгрузку больших объёмов выгоднее формировать на сервере.
Чем ЗаписатьТекст отличается от ЗаписатьБезОбработки?
ЗаписатьТекст экранирует <, > и &. ЗаписатьБезОбработки вставляет строку как есть: подходит для готового фрагмента XML, но ломает файл, если в данных есть спецсимволы.
Как записать пустое значение, чтобы получатель понял, что поле есть?
Откройте и сразу закройте элемент: получится <Поле/>. Если схема требует явный признак пустоты, пишите атрибут xsi:nil="true" с объявленным пространством xsi.
Почему GUID в файле не совпадает с кодом справочника?
XMLСтрока() от ссылки возвращает уникальный идентификатор объекта, а не код. Нужен код — выбирайте поле Код в запросе и пишите его отдельно.
Можно ли дописать элементы в уже существующий XML-файл?
ЗаписьXML пишет поток с начала и дописывание не поддерживает. Прочитайте файл в ДокументDOM, добавьте узлы и запишите заново через ЗаписьDOM, либо сформируйте файл целиком повторно.