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