При интеграции систем через XML и работе с XDTO-пакетами разработчики 1С часто сталкиваются с ситуацией: фабрика XDTO успешно загружает файл схемы .xsd, однако при попытке вызвать метод Фабрика.Тип(URI, Имя) возвращается Неопределено, а последующий вызов Фабрика.Создать(...) завершается ошибкой времени выполнения.
Разберем детально, почему так происходит, как устроена объектная модель XDTO в платформе 1С:Предприятие и как правильно создавать и заполнять объекты в подобных ситуациях.
Проанализируем фрагмент исходной XSD-схемы, который приводит к ошибке:
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.forum.mista.ru">
<xs:element name="batch">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="row">
...
</xs:element>
</xs:sequence>
<xs:attribute name="descriptions" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:schema>
Обратим внимание на то, как объявлен узел batch. В схеме присутствует корневой элемент <xs:element name="batch">, но внутри него расположен тег <xs:complexType> без собственного атрибута name. В терминологии XML Schema такой тип называется анонимным.
Метод платформы ФабрикаXDTO.Тип(<URIПространстваИмен>, <Имя>) выполняет поиск только среди глобальных именованных типов пакета (объявленных через <xs:complexType name="..."> или <xs:simpleType name="...">). Так как тип внутри batch безымянный, метод не может его найти и возвращает Неопределено.
Сам элемент batch регистрируется в фабрике 1С не как тип, а как глобальное свойство пакета — объект СвойствоXDTO.
Если мы не можем менять исходную схему XSD (например, она предоставлена сторонней системой или государственным шлюзом), мы должны извлечь анонимный тип программно через метаданные пакета XDTO.
Разберем по шагам правильный алгоритм получения типа и создания объекта:
СоздатьФабрикуXDTO.Пакеты.КорневыеСвойства пакета по имени XML-элемента.Тип и передаем его в метод Создать.Посмотрим на пример реализации на встроенном языке 1С:
// Создаем фабрику на основе файла XSD
ПутьКСхеме = "C:\Users\Администратор\Desktop\stocks.xsd";
Фабрика = СоздатьФабрикуXDTO(ПутьКСхеме);
URI = "http://www.forum.mista.ru";
// Получаем пакет и корневое свойство
Пакет = Фабрика.Пакеты.Получить(URI);
КорневоеСвойство = Пакет.КорневыеСвойства.Получить("batch");
// Получаем тип и создаем корневой объект
ТипBatch = КорневоеСвойство.Тип;
ПакетСообщений = Фабрика.Создать(ТипBatch);
// Заполняем атрибут корневого элемента
ПакетСообщений.descriptions = "Выгрузка остатков";
Рассмотрим структуру глубже. В схеме присутствует вложенный элемент row, который также имеет анонимный тип и может повторяться многократно (maxOccurs="unbounded"). Это означает, что в объекте 1С свойство row является коллекцией (СписокXDTO).
Чтобы добавить строку, нам потребуется получить тип вложенного элемента из свойств родительского типа:
// Извлекаем тип вложенной строки
СвойствоRow = ТипBatch.Свойства.Получить("row");
ТипRow = СвойствоRow.Тип;
// Создаем и заполняем новую строку
НоваяСтрока = Фабрика.Создать(ТипRow);
НоваяСтрока.store = "Центральный склад";
НоваяСтрока.date = ТекущаяДата();
НоваяСтрока.product = 1005;
НоваяСтрока.amount = 42;
// Добавляем созданный объект в список родителя
ПакетСообщений.row.Добавить(НоваяСтрока);
Поскольку корневой объект ПакетСообщений создан на основе анонимного типа, при стандартной записи через Фабрика.ЗаписатьXML(ЗаписьXML, ПакетСообщений) платформа не сможет автоматически определить имя корневого XML-тега. Это приведет либо к ошибке, либо к формированию некорректного XML-документа.
Чтобы сформировать файл с правильным именем корневого тега, необходимо обязательно передавать имя элемента и его пространство имен третьим и четвертым параметрами метода ЗаписатьXML:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\stocks.xml", "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
// Явно передаем локальное имя "batch" и URI пространства имен
Фабрика.ЗаписатьXML(ЗаписьXML, ПакетСообщений, "batch", URI);
ЗаписьXML.Закрыть();
Если у нас есть возможность редактировать саму схему XSD, наиболее удобным и чистым решением будет разделение объявления типа и объявления корневого элемента.
Рассмотрим, как модифицировать схему:
<?xml version="1.0" encoding="utf-16"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.forum.mista.ru"
targetNamespace="http://www.forum.mista.ru">
<!-- Объявляем именованный глобальный тип -->
<xs:complexType name="BatchType">
<xs:sequence>
<xs:element maxOccurs="unbounded" name="row">
<xs:complexType>
<xs:sequence>
<xs:element name="store" type="xs:string" />
<xs:element name="date" type="xs:date" />
<xs:element name="product" type="xs:unsignedShort" />
<xs:element name="amount" type="xs:unsignedByte" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="descriptions" type="xs:string" use="required" />
</xs:complexType>
<!-- Привязываем именованный тип к корневому элементу -->
<xs:element name="batch" type="tns:BatchType"/>
</xs:schema>
После добавления имени типа (в данном примере BatchType) стандартный вызов метода Фабрика.Тип() начнет работать штатно:
URI = "http://www.forum.mista.ru";
ТипBatch = Фабрика.Тип(URI, "BatchType");
ПакетСообщений = Фабрика.Создать(ТипBatch);
Такой подход делает код более читаемым, избавляет от необходимости обходить коллекцию КорневыеСвойства и упрощает последующую поддержку интеграции.