При интеграции 1С с внешними сервисами разработчики часто сталкиваются с необходимостью формирования файлов XML по предоставленной схеме XSD. Стандартный подход предполагает импорт схемы в конфигурацию в качестве XDTO-пакета либо динамическое создание фабрики XDTO, после чего разработчик получает тип через метод Фабрика.Тип() и создает экземпляр объекта. Однако нередко возникает ситуация, когда при обращении к типу платформа возвращает Неопределено, а все ключевые реквизиты схемы в конфигураторе отображаются не в ветке «Типы объектов», а в секции «Свойства» пакета XDTO. Разберем, почему так происходит и как правильно сформировать XML-документ в этом случае.
Проанализируем структуру схемы XSD. В спецификации XML Schema корневой элемент (тег xs:element) может быть определен двумя способами:
xs:element задан атрибут type, который ссылается на отдельно объявленный xs:complexType с конкретным именем. В этом случае платформа 1С помещает тип в раздел «Типы объектов» пакета XDTO, и к нему можно напрямую обратиться через Фабрика.Тип(URIПространстваИмен, ИмяТипа).xs:complexType) находится прямо внутри тега xs:element и не имеет собственного атрибута name.Если тип элемента является анонимным, платформа 1С не формирует для него глобальный тип объекта верхнего уровня. Вместо этого корневой элемент помещается в коллекцию свойств пакета XDTO. Поскольку имени типа не существует, вызов метода Фабрика.Тип(URI, Имя) возвращает Неопределено, и создать объект напрямую привычным способом не удается.
Рассмотрим, как программно обойти ограничение анонимных типов, обратившись к свойствам пакета XDTO. Получить тип корневого узла можно непосредственно из коллекции КорневыеСвойства пакета, после чего фабрика сможет создать экземпляр объекта.
Посмотрим на пример работы со схемой, загруженной в метаданные конфигурации:
URIПространстваИмен = "http://v8.1c.ru/edi/edi_stnd/109";
ПакетXDTO = ФабрикаXDTO.Пакеты.Получить(URIПространстваИмен);
// Получаем корневое свойство пакета по имени или индексу
СвойствоКорневое = ПакетXDTO.КорневыеСвойства.Получить("СчетПК");
СчетПКТип = СвойствоКорневое.Тип;
// Создаем объект XDTO на основании полученного анонимного типа
СчетПК = ФабрикаXDTO.Создать(СчетПКТип);
// Заполняем реквизиты объекта
СчетПК.ДатаФормирования = Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd");
СчетПК.НаименованиеОрганизации = "ООО ТД Комплексный";
СчетПК.ИНН = "7701234567";
СчетПК.РасчетныйСчетОрганизации = "40702810100000000001";
СчетПК.ИдПервичногоДокумента = Строка(Новый УникальныйИдентификатор);
// Выгружаем объект в XML-файл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\output.xml", "windows-1251");
ЗаписьXML.ЗаписатьОбъявлениеXML();
// Важно передать локальное имя корневого элемента третьим параметром
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, СчетПК, "СчетПК");
ЗаписьXML.Закрыть();
Если внутри создаваемого объекта есть дочерние узлы, которые также описаны анонимными комплексными типами, их нельзя создать по прямому имени. Получать их типы нужно по цепочке через свойство родителя:
// Получаем тип вложенного объекта из свойств родительского типа
СвойствоСотрудник = СчетПКТип.Свойства.Получить("Сотрудник");
ОбъектСотрудник = ФабрикаXDTO.Создать(СвойствоСотрудник.Тип);
// Заполняем поля вложенного объекта
ОбъектСотрудник.ФИО = "Иванов Иван Иванович";
ОбъектСотрудник.ТабельныйНомер = "00001";
// Если свойство множественное (список), добавляем через метод Добавить()
СчетПК.Сотрудники.Добавить(ОбъектСотрудник);
В конфигурацию схему загружать необязательно. Разберем вариант, когда схема XSD хранится во внешнем файле. Мы создаем изолированную фабрику XDTO с помощью функции СоздатьФабрикуXDTO() и выполняем выгрузку данных.
Посмотрим на практический пример:
ПутьКСхемеXSD = "C:\temp\schema.xsd";
ПутьКВыходномуXML = "C:\temp\result.xml";
МассивСхем = Новый Массив;
МассивСхем.Добавить(ПутьКСхемеXSD);
// Создаем отдельную фабрику XDTO на основе переданных файлов XSD
Фабрика = СоздатьФабрикуXDTO(МассивСхем);
URIПространства = "http://v8.1c.ru/edi/edi_stnd/109";
Пакет = Фабрика.Пакеты.Получить(URIПространства);
// Извлекаем тип через коллекцию корневых свойств пакета
СвойствоДокумента = Пакет.КорневыеСвойства.Получить("СчетПК");
ДокументОбъект = Фабрика.Создать(СвойствоДокумента.Тип);
// Заполняем значения
ДокументОбъект.ДатаФормирования = Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd");
ДокументОбъект.НаименованиеОрганизации = ОрганизацияНаименованиеПолное;
ДокументОбъект.ИНН = ОрганизацияИНН;
ДокументОбъект.РасчетныйСчетОрганизации = НомерСчета;
ДокументОбъект.ИдПервичногоДокумента = Строка(Док.УникальныйИдентификатор());
// Записываем сформированный документ в файл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКВыходномуXML, "windows-1251");
ЗаписьXML.ЗаписатьОбъявлениеXML();
Фабрика.ЗаписатьXML(ЗаписьXML, ДокументОбъект, "СчетПК");
ЗаписьXML.Закрыть();
Проанализируем технические особенности, на которые необходимо обратить внимание при формировании XML:
maxOccurs="unbounded" или число больше единицы, свойство в 1С инициализируется как СписокXDTO. Присваивать значение такому свойству через оператор равенства нельзя — возникнет ошибка времени выполнения. Заполнение выполняется строго через метод Добавить() соответствующего списка.ФабрикаXDTO.ЗаписатьXML() параметр НазначениеТипаXML по умолчанию имеет значение, исключающее явную запись атрибута xsi:type. Для объектов с анонимными типами нельзя устанавливать параметр в значение НазначениеТипаXML.Явное, так как у типа нет имени, и выгруженный XML будет содержать некорректные атрибуты либо вызовет ошибку валидации.ЗаписатьXML() обязательно передавайте третий параметр — имя корневого элемента (например, "СчетПК"), чтобы платформа корректно сформировала открывающий и закрывающий теги.Если структура схемы находится под вашим контролем или есть готовые образцы выгрузки, рассмотрим еще два альтернативных варианта:
xs:complexType за пределы xs:element, назначить ему явное имя (например, name="СчетПКТип") и сослаться на него из элемента через атрибут type="tns:СчетПКТип". После импорта в 1С данный тип сразу отобразится в ветке «Типы объектов», а код станет стандартным с обращением через Фабрика.Тип(...).ФабрикаXDTO.ПрочитатьXML(). Платформа самостоятельно воссоздаст дерево объектов с сохранением всех анонимных типов. После этого останется лишь изменить значения полей и сохранить объект обратно в XML-файл.