При интеграции 1С с внешними системами (веб-сервисы, интернет-магазины, банковские шлюзы) мы часто сталкиваемся с необходимостью сформировать XML-файл определенной структуры. Казалось бы, задача тривиальная, но стандартные методы платформы не всегда дают нужный результат "из коробки". В таких случаях разработчики часто используют специализированные инструменты, такие как адаптивная выгрузка и загрузка данных XML, позволяющая гибко настраивать обмен между конфигурациями.
В этой статье мы подробно разберем, почему простой метод ЗаписатьXML выдает ошибку при попытке записать Структуру, рассмотрим готовые универсальные функции для быстрой конвертации, а также изучим профессиональный подход с использованием XDTO для сложных схем данных.
Многие разработчики первым делом пытаются использовать прямой метод записи, ожидая, что платформа сама разберет структуру по ключам. Посмотрим на типичный пример, который вызывает ошибку:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8");
Значение = Новый Структура("Поле1", "Значение1");
// Следующая строка вызовет исключение:
// "Ошибка при вызове метода контекста (ЗаписатьXML): Значения данного типа не могут быть записаны в XML"
ЗаписатьXML(ЗаписьXML, Значение);
Почему так происходит? Согласно документации (Синтакс-Помощнику), метод ЗаписатьXML поддерживает ограниченный набор типов: Число, Строка, Дата, Булево, ссылочные типы и объекты XDTO. Тип Структура или Соответствие в этот список не входит, так как платформа не знает, как именно вы хотите отобразить эти данные (нужны ли пространства имен, атрибуты или вложенные узлы).
Поэтому нам необходимо реализовать собственный алгоритм обхода коллекции или применить универсальный модуль «Трансформер» для конвертации JSON/XML/CSV, который берет на себя рутинные операции по преобразованию форматов.
Если вам нужно сформировать простой XML без сложной иерархии пространств имен и атрибутов, самым эффективным решением будет использование рекурсивной функции. Она будет проходить по всем ключам структуры и записывать их как теги.
Разберем готовый пример функции для сериализации (преобразования в XML). Мы будем использовать объект ЗаписьXML.
Функция СтруктураВXML(СтруктураДанных, Подструктура = Ложь, Запись = Неопределено) Экспорт
// Если это первый вызов (корень), инициализируем запись
Если Не Подструктура Тогда
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку(); // Или укажите имя файла
КонецЕсли;
// Обходим коллекцию
Для Каждого КлючЗначение Из СтруктураДанных Цикл
// Начинаем элемент с именем ключа структуры
Запись.ЗаписатьНачалоЭлемента(КлючЗначение.Ключ);
Значение = КлючЗначение.Значение;
// Рекурсивный вызов, если внутри лежит другая Структура
Если ТипЗнч(Значение) = Тип("Структура") Тогда
СтруктураВXML(Значение, Истина, Запись);
// Обработка простых типов (можно расширить для Дат и Булево)
ИначеЕсли ЗначениеЗаполнено(Значение) Тогда
Запись.ЗаписатьТекст(Строка(Значение));
КонецЕсли;
// Закрываем элемент
Запись.ЗаписатьКонецЭлемента();
КонецЦикла;
// Возвращаем строку XML, если это был корневой вызов
Если Не Подструктура Тогда
Возврат Запись.Закрыть();
КонецЕсли;
КонецФункции
Обратите внимание: данный метод подходит, если имена ключей вашей структуры соответствуют правилам именования XML-тегов (без пробелов, не начинаются с цифр). Если вам требуется переносить не просто структуру, а, например, движения документов через XML, логика обхода будет сложнее из-за необходимости обработки наборов записей регистров — в таких случаях поможет универсальная выгрузка документов 1С в XML и JSON.
Чтобы прочитать полученный XML обратно в структуру 1С, удобнее всего воспользоваться моделью DOM (Document Object Model). Объект ПостроительDOM считывает весь файл в память и позволяет удобно обходить дерево узлов.
Рассмотрим функцию десериализации:
Функция XMLВСтруктуру(ТекстXML = Неопределено, УзелDOM = Неопределено, СтруктураРезультат = Неопределено) Экспорт
// Инициализация при первом вызове
Если ТекстXML <> Неопределено Тогда
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(ТекстXML);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
СтруктураРезультат = Новый Структура;
УзелDOM = ДокументDOM; // Начинаем с корня
КонецЕсли;
// Обходим дочерние узлы
Для Каждого Элемент Из УзелDOM.ДочерниеУзлы Цикл
// Если внутри узла просто текст (например - 123
)
Если Элемент.ПервыйДочерний <> Неопределено
И ТипЗнч(Элемент.ПервыйДочерний) = Тип("ТекстDOM") Тогда
СтруктураРезультат.Вставить(Элемент.ИмяУзла, Элемент.ПервыйДочерний.Данные);
// Если внутри есть вложенные теги (элементы)
ИначеЕсли Элемент.ПервыйДочерний <> Неопределено
И ТипЗнч(Элемент.ПервыйДочерний) = Тип("ЭлементDOM") Тогда
НовоеЗначениеСтруктура = Новый Структура;
СтруктураРезультат.Вставить(Элемент.ИмяУзла, НовоеЗначениеСтруктура);
// Рекурсивный спуск
XMLВСтруктуру(, Элемент, НовоеЗначениеСтруктура);
КонецЕсли;
КонецЦикла;
Возврат СтруктураРезультат;
КонецФункции
Этот подход имеет ограничение: в Структуре ключи должны быть уникальны. Если вы работаете с файлами выгрузки конфигурации, вам может быть полезен конвертер обработок XML в EPF, который автоматизирует преобразование XML-описаний в готовые объекты платформы.
Если вы взаимодействуете с серьезным веб-сервисом (SOAP) или государственной системой, вам часто требуется:
В этом случае ручное склеивание XML или использование простых структур приведет к ошибкам. Правильный путь — использование ФабрикаXDTO. Для упрощения подготовки модели данных можно использовать инструмент для конвертации схемы XSD в модель XDTO.
Рассмотрим, как облегчить работу с фабрикой, создав небольшую библиотеку оберток. Это позволит писать код быстрее и чище.
Шаг 1. Вспомогательная функция для создания объекта XDTO:
// Создает объект XDTO или значение простого типа
Функция СоздатьЭлементXDTO(Знач Владелец, Знач Имя, Знач ЗначениеСвойства = "") Экспорт
// Определяем свойство из пакета или объекта-владельца
Если ТипЗнч(Владелец) = Тип("ПакетXDTO") Тогда
СвойствоXDTO = Владелец.КорневыеСвойства.Получить(Имя);
Иначе
СвойствоXDTO = Владелец.Свойства.Получить(Имя);
КонецЕсли;
ТипXDTO = СвойствоXDTO.Тип;
// Если тип простой (строка, число), сразу заполняем значение
Если ТипЗнч(ТипXDTO) = Тип("ТипЗначенияXDTO") Тогда
ОбъектXDTO = ФабрикаXDTO.Создать(ТипXDTO, ЗначениеСвойства);
Иначе
// Если тип сложный, создаем пустой объект
ОбъектXDTO = ФабрикаXDTO.Создать(ТипXDTO);
КонецЕсли;
Возврат ОбъектXDTO;
КонецФункции
Шаг 2. Вспомогательная функция для добавления свойства в список:
В XDTO массивы (списки) требуют особого подхода. Мы не можем просто присвоить значение, нам нужно использовать метод Добавить() коллекции.
Функция СоздатьДобавитьЭлементСпискаXDTO(Знач Владелец, Знач Имя, Знач ЗначениеСвойства = "") Экспорт
Элемент = СоздатьЭлементXDTO(Владелец, Имя, ЗначениеСвойства);
Владелец[Имя].Добавить(Элемент);
Возврат Элемент;
КонецФункции
Шаг 3. Итоговая сериализация:
Функция СериализоватьВСтроку(Знач ОбъектXDTO, Знач ИмяКорневогоТега) Экспорт
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
// Третий параметр важен для указания имени корня,
// так как сам объект XDTO может его не знать в контексте записи
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектXDTO, ИмяКорневогоТега);
Возврат ЗаписьXML.Закрыть();
КонецФункции
Предположим, у нас в конфигурации есть Пакет XDTO с URI "http://mycompany.ru/exchange". Мы можем заполнить данные следующим образом:
// Получаем пакет
Пакет = ФабрикаXDTO.Пакеты.Получить("http://mycompany.ru/exchange");
// Создаем корневой объект (предположим, он называется DocumentData)
ДанныеДокумента = СоздатьЭлементXDTO(Пакет, "DocumentData");
// Заполняем простые поля
ДанныеДокумента.Number = "001";
ДанныеДокумента.Date = ТекущаяДата();
// Заполняем список товаров (вложенный список Goods -> Item)
Товары = СоздатьЭлементXDTO(ДанныеДокумента, "Goods");
ДанныеДокумента.Goods = Товары;
// Добавляем строки
Для Каждого Стр Из ТЧТовары Цикл
ЭлементТовара = СоздатьДобавитьЭлементСпискаXDTO(Товары, "Item");
ЭлементТовара.Name = Стр.Номенклатура.Наименование;
ЭлементТовара.Quantity = Стр.Количество;
КонецЦикла;
// Получаем итоговый XML
РезультатXML = СериализоватьВСтроку(ДанныеДокумента, "DocumentData");
Структура -> ЗаписьXML.ПостроительDOM, преобразуя дерево узлов во вложенные структуры, или универсальную выгрузку и загрузку XML с дополнительными опциями отбора — для этого подойдёт универсальный обмен данными XML по правилам конвертации.ФабрикаXDTO. Это потребует предварительного описания типов (схемы), но гарантирует корректность типов данных и пространств имен.