Работа с механизмами XDTO (XML Data Transfer Objects) в платформе 1С:Предприятие часто вызывает затруднения у начинающих разработчиков. Типичные проблемы возникают на этапе описания структуры в схеме XSD, при попытке добавить элементы во вложенный список, а также из-за ошибок валидации закрытых типов данных. Разберем по шагам, как построить корректную схему XSD, связать её с платформой 1С и программно выгрузить данные в XML без обходных путей.
Представим, что перед нами стоит задача сформировать следующий XML-документ:
<?xml version="1.0" encoding="utf-8"?>
<ROOT>
<ArchivedLocalStocks>
<ArchivedLocalStock WAREH_CODE="000000002" LOCALCODE="00000000100" LOT_ID="" DATE="2014-03-01T00:00:00" STOCK="438.000" DTLM="20140623 11:53" />
<ArchivedLocalStock WAREH_CODE="000000002" LOCALCODE="00000000103" LOT_ID="" DATE="2014-03-01T00:00:00" STOCK="192.000" DTLM="20140623 11:53" />
</ArchivedLocalStocks>
</ROOT>
Проанализируем типичные ошибки, которые допускаются при создании схемы для подобного документа:
<xs:element name="ArchivedLocalStock"> без указания имени типа, фабрика 1С назначит ему системное составное имя. Получить такой тип через ФабрикаXDTO.Тип(...) в программном коде станет крайне неудобно.<xs:complexType name="ROOT" /> и <xs:complexType name="ArchivedLocalStocks" /> полностью лишает фабрику информации о дочерних элементах.<xs:extension base="xs:anyType"> не требуется, если элемент содержит только набор XML-атрибутов.DATE имеет тип xs:dateTime, поэтому передавать в него произвольную текстовую строку нельзя — платформа 1С ожидает стандартное значение типа Дата. Поле DTLM содержит нестандартный формат "20140623 11:53", который не укладывается в спецификацию XML Schema для даты и времени, поэтому его необходимо объявлять строго как xs:string.В объектной модели XDTO каждый тип по умолчанию является закрытым (Открытый = Ложь). Это означает, что объекту разрешено содержать исключительно те дочерние теги и атрибуты, которые в явном виде задекларированы в схеме XSD.
Когда сложные типы объявляются пустыми, а разработчик пытается вызвать метод добавления произвольных данных:
xdtoArchivedLocalStoks.Добавить(ФормаXML.Элемент, "ArchivedStocks", "ArchivedLocalStock", xdtoArchivedLocalStok);
платформа 1С выдает ошибку, запрещая вставку не предусмотренных схемой свойств. Ручная установка признака Открытый = Истина в свойствах пакета XDTO снимает ограничение, но превращает строгую модель в неконтролируемый контейнер (аналог xs:any). Правильное решение состоит в том, чтобы корректно описать структуру вложенности в самой схеме XSD.
Построим схему, в которой выделен корневой элемент, а вложенный тип строки вынесен в отдельный именованный тип данных:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="ArchivedStocks"
xmlns:tns="ArchivedStocks"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<!-- Описание атрибутов строки остатка -->
<xs:complexType name="ArchivedLocalStock">
<xs:attribute name="WAREH_CODE" type="xs:string" use="required" />
<xs:attribute name="LOCALCODE" type="xs:string" use="required" />
<xs:attribute name="LOT_ID" type="xs:string" />
<xs:attribute name="DATE" type="xs:dateTime" use="required" />
<xs:attribute name="STOCK" type="xs:decimal" use="required" />
<xs:attribute name="DTLM" type="xs:string" />
</xs:complexType>
<!-- Описание списка строк -->
<xs:complexType name="ArchivedLocalStocks">
<xs:sequence>
<xs:element name="ArchivedLocalStock" type="tns:ArchivedLocalStock" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<!-- Описание корневого типа -->
<xs:complexType name="ROOT">
<xs:sequence>
<xs:element name="ArchivedLocalStocks" type="tns:ArchivedLocalStocks" />
</xs:sequence>
</xs:complexType>
<!-- Корневой элемент документа -->
<xs:element name="ROOT" type="tns:ROOT" />
</xs:schema>
Рассмотрим подробнее программную работу с объектами и коллекциями XDTO. Когда элемент объявлен с атрибутом maxOccurs="unbounded", обращение к нему в коде возвращает коллекцию типа СписокXDTO. Метод Добавить() у такой коллекции принимает ровно один параметр — уже созданный и заполненный ОбъектXDTO.
Для одиночных вложенных элементов метод Добавить() вызывать не нужно: достаточно присвоить созданный дочерний объект соответствующему свойству родителя.
Разберем по шагам рабочий пример выгрузки:
// Задаем пространство имен согласно схеме
ПространствоИмен = "ArchivedStocks";
// Получаем типы XDTO из глобальной фабрики
ТипROOT = ФабрикаXDTO.Тип(ПространствоИмен, "ROOT");
ТипКоллекции = ФабрикаXDTO.Тип(ПространствоИмен, "ArchivedLocalStocks");
ТипСтроки = ФабрикаXDTO.Тип(ПространствоИмен, "ArchivedLocalStock");
// Создаем корневой объект и узел для списка
xdtoROOT = ФабрикаXDTO.Создать(ТипROOT);
xdtoArchivedLocalStocks = ФабрикаXDTO.Создать(ТипКоллекции);
// Заполняем строки остатков в цикле
Для Индекс = 1 По 3 Цикл
xdtoСтрока = ФабрикаXDTO.Создать(ТипСтроки);
xdtoСтрока.WAREH_CODE = "000000002";
xdtoСтрока.LOCALCODE = "0000000010" + Строка(Индекс);
xdtoСтрока.LOT_ID = "";
// В поле типа xs:dateTime передаем стандартную Дату 1С
xdtoСтрока.DATE = '20140301000000';
xdtoСтрока.STOCK = 100 * Индекс;
xdtoСтрока.DTLM = "20140623 11:53";
// Добавляем созданный объект в коллекцию СписокXDTO
xdtoArchivedLocalStocks.ArchivedLocalStock.Добавить(xdtoСтрока);
КонецЦикла;
// Привязываем заполненную коллекцию к корневому объекту
xdtoROOT.ArchivedLocalStocks = xdtoArchivedLocalStocks;
// Записываем сформированные данные в файл XML
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл("C:\ArchivedStocks.xml", "UTF-8");
Запись.ЗаписатьОбъявлениеXML();
// Метод сериализации запишет узел ROOT с учетом схемы пакета
ФабрикаXDTO.ЗаписатьXML(Запись, xdtoROOT);
Запись.Закрыть();
При реализации интеграций через XDTO полезно учитывать следующие нюансы платформы 1С:
СоздатьФабрикуXDTO(ПутьКФайлуСхемы), что позволит избежать постоянных доработок и обновлений конфигурации.ФабрикаXDTO создает модель данных полностью в оперативной памяти. Если требуется выгружать сотни тысяч строк, это может привести к ошибке нехватки памяти. В таких сценариях эффективнее использовать потоковую прямую запись через методы ЗаписьXML.ЗаписатьНачалоЭлемента(), ЗаписьXML.ЗаписатьАтрибут() и ЗаписьXML.ЗаписатьКонецЭлемента().