Запись XML из 1С 8: код, кодировка, атрибуты и большие выгрузки
Содержание
Записать XML файл в 1С 8.3 проще всего объектом ЗаписьXML: ОткрытьФайл() → ЗаписатьОбъявлениеXML() → пары ЗаписатьНачалоЭлемента() / ЗаписатьКонецЭлемента() → Закрыть(). Объект пишет поток последовательно, сам экранирует спецсимволы и не держит документ в памяти.
Частое заблуждение — собирать XML конкатенацией строк: "<Товар>" + Наименование + "</Товар>". Первый же контрагент с амперсандом в названии («Рога & Копыта») сломает файл, а получатель увидит ошибку разбора. ЗаписьXML превращает & в & автоматически. Строки оставьте для логов, а файлы обмена пишите объектом.
Что решить до первой строки кода
Перед программированием ответьте себе на пять вопросов. От них зависит выбор метода и половина будущих ошибок.
- 📄 Кто читает файл. Внешняя система с описанной схемой (XSD) — значит, имена элементов, порядок и пространства имён диктует она. Другая база 1С — можно сериализовать объекты целиком.
- 🔤 Кодировка. По умолчанию
ЗаписьXMLпишет в UTF-8. Старые учётные программы и банковские клиенты нередко ждутwindows-1251. - 🧭 Где выполняется код. Файл на диске создаётся там, где работает код. Серверный код пишет на диск сервера, а не на компьютер пользователя.
- 📦 Объём. Сотня строк и полмиллиона строк требуют разной техники (раздел про производительность ниже).
- 🧾 Форматы значений. Даты, числа и булево в XML пишут в каноническом виде, а не так, как их показывает форма.
Последний пункт стоит разобрать сразу. Функция Строка() форматирует значение по региональным настройкам: число 1234,5 превратится в 1 234,5 с неразрывным пробелом. Для XML нужна XMLСтрока():
| Значение в 1С | Строка() | 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 |
| Ссылка на справочник | наименование | уникальный идентификатор (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:\...") на сервере создаст файл на диске сервера. Пользователю нужен другой путь:
- На сервере запишите XML во временный файл:
ИмяФайла = ПолучитьИмяВременногоФайла("xml"). - Прочитайте его в
Новый ДвоичныеДанные(ИмяФайла)и поместите во временное хранилище черезПоместитьВоВременноеХранилище(). - Удалите временный файл:
УдалитьФайлы(ИмяФайла). - На клиенте передайте адрес хранилища в диалог сохранения. В типовых конфигурациях на БСП для этого есть
ФайловаяСистемаКлиент.СохранитьФайл().
Атрибуты, пространства имён и префиксы
Внешние схемы почти всегда требуют пространство имён. Например, файл должен начинаться так: <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 пишет поток и сама по себе экономна. Память и время съедают обычно не запись, а то, что вокруг неё.
Что реально ускоряет выгрузку на сотнях тысяч строк:
Выбрать()вместоВыгрузить(). Выборка тянет строки порциями, таблица значений целиком сидит в памяти.- Один запрос вместо запроса в цикле. Цены, остатки и реквизиты связанных справочников соедините в исходном запросе. Запрос на каждую строку — главный тормоз ручных выгрузок.
- Без обращения к реквизитам через точку в цикле.
Выборка.Номенклатура.Артикулделает скрытое чтение объекта. ВыберитеНоменклатура.Артикулполем запроса. - Без
ПолучитьОбъект()на каждую строку. Объект читает все табличные части. Если нужны три поля — берите их запросом. - Отступы выключены. Для файла в несколько сотен мегабайт отступы добавляют заметный объём.
- Файл, а не строка.
УстановитьСтроку()держит весь результат в памяти доЗакрыть(). Для больших объёмов пишите сразу в файл. - Без
ЗаписьDOMиДокументDOMдля сборки. DOM строит дерево в памяти целиком. Он хорош для правки небольших файлов, но не для генерации гигабайтных выгрузок.
Если получатель тоже 1С и файл огромный, посмотрите на ЗаписьFastInfoset. Это бинарное представление XML: файл компактнее и быстрее читается. Внешние системы такой формат поддерживают редко, поэтому согласуйте его заранее.
Выгрузку в фоновом задании пишите порциями по периодам или по диапазону кодов, каждую порцию — в отдельный файл. Сбой на третьем часу работы тогда не обнуляет весь результат.
Частые вопросы о записи XML в 1С
Можно ли записать XML в тонком клиенте без обращения к серверу?
ЗаписьXML доступна на клиенте. Но данные из базы всё равно придут с сервера, поэтому выгрузку больших объёмов выгоднее формировать на сервере.Чем ЗаписатьТекст отличается от ЗаписатьБезОбработки?
ЗаписатьТекст экранирует <, > и &. ЗаписатьБезОбработки вставляет строку как есть: подходит для готового фрагмента XML, но ломает файл, если в данных есть спецсимволы.Как записать пустое значение, чтобы получатель понял, что поле есть?
<Поле/>. Если схема требует явный признак пустоты, пишите атрибут xsi:nil="true" с объявленным пространством xsi.Почему GUID в файле не совпадает с кодом справочника?
XMLСтрока() от ссылки возвращает уникальный идентификатор объекта, а не код. Нужен код — выбирайте поле Код в запросе и пишите его отдельно.Можно ли дописать элементы в уже существующий XML-файл?
ЗаписьXML пишет поток с начала и дописывание не поддерживает. Прочитайте файл в ДокументDOM, добавьте узлы и запишите заново через ЗаписьDOM, либо сформируйте файл целиком повторно.