Почему метод ФабрикаXDTO.Тип возвращает Неопределено и как создать объект по схеме XSD?

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

При интеграции систем через 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.

Решение 1. Программное получение анонимного типа из корневого свойства

Если мы не можем менять исходную схему XSD (например, она предоставлена сторонней системой или государственным шлюзом), мы должны извлечь анонимный тип программно через метаданные пакета XDTO.

Разберем по шагам правильный алгоритм получения типа и создания объекта:

  1. Создаем фабрику XDTO на основе файла схемы с помощью функции СоздатьФабрикуXDTO.
  2. Получаем объект пакета по пространству имен через коллекцию Пакеты.
  3. Находим нужное свойство в коллекции КорневыеСвойства пакета по имени XML-элемента.
  4. Извлекаем тип свойства через его атрибут Тип и передаем его в метод Создать.

Посмотрим на пример реализации на встроенном языке 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;
ЗаписьXML.ОткрытьФайл("C:\stocks.xml", "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();

// Явно передаем локальное имя "batch" и URI пространства имен
Фабрика.ЗаписатьXML(ЗаписьXML, ПакетСообщений, "batch", URI);

ЗаписьXML.Закрыть();

Решение 2. Корректировка XSD-схемы (выделение именованного типа)

Если у нас есть возможность редактировать саму схему 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);

Такой подход делает код более читаемым, избавляет от необходимости обходить коллекцию КорневыеСвойства и упрощает последующую поддержку интеграции.

← На главную