Как правильно сформировать XML-файл в 1С с помощью объекта ЗаписьXML: разбираем элементы, атрибуты и текстовые узлы

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При выгрузке данных из 1С в сторонние системы (например, при подготовке зарплатных реестров для банков по стандарту Wages.xsd) разработчики часто сталкиваются с необходимостью ручного формирования XML-файлов. Платформа 1С предоставляет для этих целей удобный низкоуровневый объект ЗаписьXML. Однако у программистов, которые редко работают с разметкой XML, часто возникает путаница между понятиями «элемент» (тег), «атрибут» и «текстовое содержимое узла».

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

В чем суть проблемы: разница между атрибутом и элементом

Рассмотрим ситуацию. Нам необходимо получить XML-структуру следующего вида:


<Сотрудник Нпп="1">
    <Фамилия>Иванов</Фамилия>
    <Имя>Иван</Имя>
    <Отчество>Иванович</Отчество>
</Сотрудник>

Обратите внимание на ключевые особенности этой структуры:

  1. Сотрудник — это главный (родительский) элемент.
  2. Нпп — это атрибут элемента Сотрудник. Он записывается внутри открывающего тега.
  3. Фамилия, Имя, Отчество — это подчиненные (дочерние) элементы. Они находятся внутри тега Сотрудник и содержат внутри себя текстовую информацию.

Типичная ошибка новичка заключается в попытке записать всё через атрибуты. Изначальный ошибочный код выглядел так:


ЗаписьXML.ЗаписатьНачалоЭлемента("Сотрудник");
ЗаписьXML.ЗаписатьАтрибут("Фамилия", Строка(МассивВыгрузки[ИндексМассива][0]));
// ... и так далее для всех полей
ЗаписьXML.ЗаписатьКонецЭлемента();

Такой код сгенерирует плоскую строку, где все персональные данные сотрудника станут его атрибутами:

<Сотрудник Фамилия="Иванов" Имя="Иван" ... />

Это грубое нарушение схемы XML, и принимающий шлюз банка отклонит такой файл.

Вторая распространенная ошибка — создание пустого тега с атрибутом вместо текста:


ЗаписьXML.ЗаписатьНачалоЭлемента("Фамилия");
ЗаписьXML.ЗаписатьАтрибут("Фамилия", "Иванов");
ЗаписьXML.ЗаписатьКонецЭлемента();

Этот код выдаст результат <Фамилия Фамилия="Иванов"/>, что также является неверным.

---

Пошаговое решение: пишем правильный код для ЗаписьXML

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

Для правильной записи структуры нам понадобятся три основных метода:

Давайте по шагам соберем цикл выгрузки сотрудников:

Шаг 1. Открываем родительский элемент и записываем атрибут

Сначала мы открываем тег Сотрудник и сразу же добавляем к нему атрибут порядкового номера (Нпп):


ЗаписьXML.ЗаписатьНачалоЭлемента("Сотрудник");
ЗаписьXML.ЗаписатьАтрибут("Нпп", Строка(ИндексМассива + 1));

Шаг 2. Записываем дочерние текстовые элементы

Теперь, не закрывая элемент Сотрудник, мы последовательно создаем вложенные теги для фамилии, имени и отчества. Для каждого из них мы открываем тег, пишем текст и закрываем тег:


// Выгружаем Фамилию
ЗаписьXML.ЗаписатьНачалоЭлемента("Фамилия");
ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][0]));
ЗаписьXML.ЗаписатьКонецЭлемента();

// Выгружаем Имя
ЗаписьXML.ЗаписатьНачалоЭлемента("Имя");
ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][1]));
ЗаписьXML.ЗаписатьКонецЭлемента();

// Выгружаем Отчество
ЗаписьXML.ЗаписатьНачалоЭлемента("Отчество");
ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][2]));
ЗаписьXML.ЗаписатьКонецЭлемента();

Шаг 3. Закрываем родительский элемент

После того как все подчиненные данные конкретного сотрудника записаны, нам необходимо закрыть основной тег Сотрудник:

ЗаписьXML.ЗаписатьКонецЭлемента(); // Закрывает </Сотрудник> ---

Полный листинг корректного кода выгрузки

Ниже представлен готовый законченный алгоритм выгрузки реестра, написанный с учетом правильной вложенности тегов и корректного форматирования данных:


ЗаписьXML = Новый ЗаписьXML;
// Открываем файл с явным указанием кодировки (обычно для банков это windows-1251)
ЗаписьXML.ОткрытьФайл(ДиалогСохраненияФайла.ПолноеИмяФайла, "windows-1251");
ЗаписьXML.ЗаписатьОбъявлениеXML();

// Корневой элемент реестра со служебными атрибутами
ЗаписьXML.ЗаписатьНачалоЭлемента("СчетаПК");
ЗаписьXML.ЗаписатьАтрибут("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
ЗаписьXML.ЗаписатьАтрибут("xsi:noNamespaceSchemaLocation", "Wages.xsd");
ЗаписьXML.ЗаписатьАтрибут("ДатаФормирования", Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd"));
ЗаписьXML.ЗаписатьАтрибут("НомерДоговора", "123-П");
ЗаписьXML.ЗаписатьАтрибут("НаименованиеОрганизации", "ООО Торговый Дом");
ЗаписьXML.ЗаписатьАтрибут("ИНН", "7700000000");

// Начало блока зачисления
ЗаписьXML.ЗаписатьНачалоЭлемента("ЗачислениеЗарплаты");

Для ИндексМассива = 0 по МассивВыгрузки.Количество() - 1 Цикл
    
    // Открываем тег сотрудника с атрибутом номера по порядку
    ЗаписьXML.ЗаписатьНачалоЭлемента("Сотрудник");
    ЗаписьXML.ЗаписатьАтрибут("Нпп", Строка(ИндексМассива + 1));
    
    // Фамилия
    ЗаписьXML.ЗаписатьНачалоЭлемента("Фамилия");
    ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][0]));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Имя
    ЗаписьXML.ЗаписатьНачалоЭлемента("Имя");
    ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][1]));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Отчество
    ЗаписьXML.ЗаписатьНачалоЭлемента("Отчество");
    ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][2]));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Отделение банка
    ЗаписьXML.ЗаписатьНачалоЭлемента("ОтделениеБанка");
    ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][3]));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Номер счета
    ЗаписьXML.ЗаписатьНачалоЭлемента("ЛицевойСчет");
    ЗаписьXML.ЗаписатьТекст(Строка(МассивВыгрузки[ИндексМассива][4]));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Сумма к зачислению (с форматированием!)
    ЗаписьXML.ЗаписатьНачалоЭлемента("Сумма");
    СуммаЧислом = Число(МассивВыгрузки[ИндексМассива][5]);
    СтрокаСуммы = Формат(СуммаЧислом, "ЧДЦ=2; ЧРД=.; ЧГ=0");
    ЗаписьXML.ЗаписатьТекст(СтрокаСуммы);
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Закрываем тег Сотрудник
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
КонецЦикла;

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

// Закрываем корневой элемент СчетаПК
ЗаписьXML.ЗаписатьКонецЭлемента();

// Закрываем файл и освобождаем ресурсы
ЗаписьXML.Закрыть();
Предупреждение("Выгрузка успешно завершена!");

---

Важные технические нюансы при работе с XML для банков

При выгрузке реальных реестров в банк простая запись строк может привести к ошибкам валидации файла. Обратите внимание на следующие ключевые правила:

  1. Форматирование числовых значений (сумм). Никогда не используйте стандартную функцию Строка() для выгрузки чисел в XML. По умолчанию 1С форматирует числа с группировкой триад (например, число 150000.50 превратится в строку "150 000,50" с пробелом и запятой). Банковские парсеры не смогут прочитать такое значение. Всегда используйте явное форматирование: Формат(Значение, "ЧДЦ=2; ЧРД=.; ЧГ=0"), где точка — разделитель дробной части, а группировка отключена.
  2. Форматирование дат. Стандарт XML требует формат дат вида ГГГГ-ММ-ДД (например, 2023-10-27). Для этого применяйте маску: Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd").
  3. Экранирование спецсимволов. Если в наименовании организации или ФИО сотрудников присутствуют символы <, >, & или кавычки, метод ЗаписатьТекст() автоматически преобразует их в безопасные сущности (например, &amp;). Если же вы попытаетесь склеивать XML «вручную» обычной текстовой строкой, файл получится битым. Использование встроенного объекта ЗаписьXML гарантирует безопасность разметки.

Альтернатива: использование фабрики XDTO

Если в вашей конфигурации подключена схема Wages.xsd (или вы можете загрузить её через XDTO-пакеты), ручное написание тегов вообще не требуется. Использование фабрики XDTO — более современный и надежный способ работы с XML в 1С.

Платформа сама контролирует типы данных, обязательность заполнения реквизитов и вложенность тегов в соответствии со схемой. Рассмотрим краткий пример использования XDTO:


// Создаем объект на основе типа из схемы XDTO
РеестрXDTO = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("http://v8.1c.ru/edi/edi_stnd/109", "СчетаПК"));
РеестрXDTO.ДатаФормирования = ТекущаяДата();
РеестрXDTO.ИНН = "7700000000";

// Заполняем табличную часть сотрудников
Для Каждого СтрокаДанных Из МассивВыгрузки Цикл
    СотрудникОбъект = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("http://v8.1c.ru/edi/edi_stnd/109", "Сотрудник"));
    СотрудникОбъект.Фамилия = СтрокаДанных[0];
    СотрудникОбъект.Имя = СтрокаДанных[1];
    СотрудникОбъект.Сумма = Число(СтрокаДанных[5]); // Тип контролируется автоматически!
    
    РеестрXDTO.ЗачислениеЗарплаты.Добавить(СотрудникОбъект);
КонецЦикла;

// Запись готового XDTO-объекта в файл одной командой
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлу, "windows-1251");
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, РеестрXDTO);
ЗаписьXML.Закрыть();

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

← На главную