XDTO-пакеты: как 1С описывает и разбирает XML при обмене
Содержание
Пакет XDTO в 1С — это описание формата XML-данных внутри конфигурации: пространство имён, типы и их свойства. По сути, это XSD-схема, которую платформа понимает напрямую. Пакеты лежат в ветке Общие → XDTO-пакеты конфигуратора, а в коде работают через глобальные объекты ФабрикаXDTO и СериализаторXDTO.
Зачем они нужны: чтобы обмен с другой системой шёл по строгой схеме. Вы собираете объект по типу из пакета, платформа проверяет его и пишет корректный XML. На входе — наоборот: XML разбирается в объект, к свойствам которого обращаются через точку. Ручной разбор тегов через ЧтениеXML и склейка строк больше не нужны.
Что внутри XDTO-пакета и как устроена модель
XDTO расшифровывается как XML Data Transfer Objects. Модель держится на трёх уровнях.
- 🏭 Фабрика XDTO (
ФабрикаXDTO) — хранилище всех известных типов. Глобальная фабрика содержит пакеты конфигурации и служебные пакеты платформы. Создаёт объекты и читает/пишет XML. - 📦 Пакет XDTO — набор типов с одним URI пространства имён. Один пакет обычно соответствует одной XSD-схеме.
- 🧩 Типы — два вида: типы значений и типы объектов.
| Элемент | Чему соответствует в XSD | Что получится в коде |
|---|---|---|
Тип значения (ТипЗначенияXDTO) | simpleType | Строка, Число, Дата, Булево, ДвоичныеДанные |
Тип объекта (ТипОбъектаXDTO) | complexType | ОбъектXDTO со свойствами |
| Свойство с верхней границей больше 1 | элемент с maxOccurs > 1 | СписокXDTO |
| Свойство пакета (корневое) | глобальный element | точка входа документа |
Свойства типа объекта
У каждого свойства задаются имя, тип, нижняя и верхняя граница. Нижняя граница 0 — свойство необязательное. Верхняя граница -1 — список без ограничения.
Ещё есть форма: элемент, атрибут или текст. От неё зависит, как свойство попадёт в XML — тегом <Number> или атрибутом Number="...".
У самого типа объекта есть флаги. Самые полезные:
- Открытый — объект может содержать свойства, которых нет в описании. Полезно, когда партнёр добавляет новые поля, а ваш разбор не должен из-за этого падать.
- Упорядоченный — порядок свойств в XML строго как в описании.
- Абстрактный — тип только для наследования, создать его экземпляр нельзя.
Типы значений сужают базовый тип фасетами: длина строки, шаблон, перечисление допустимых значений, число знаков. Например, ИНН — строка длиной 10 или 12 по шаблону из цифр.
Как создать пакет XDTO в конфигураторе
Пакет можно описать вручную или получить из готовой схемы. Второй путь быстрее и надёжнее, если партнёр прислал XSD.
Импорт из XSD
- В дереве конфигурации раскройте
Общиеи найдите веткуXDTO-пакеты. - Вызовите контекстное меню на ветке и выберите
Импорт XML-схемы.... - Укажите файл
.xsd. Платформа создаст пакет с пространством имён изtargetNamespaceсхемы. - Откройте пакет и проверьте, что все типы и свойства на месте.
- Сохраните конфигурацию и обновите конфигурацию базы данных.
Если схема ссылается на другие схемы через import, сначала загрузите их. Иначе пакет не найдёт внешние типы. Связь между пакетами задаётся в редакторе пакета как импорт другого пространства имён.
Ручное описание
Для небольшого формата проще собрать пакет руками:
- Создайте новый XDTO-пакет и задайте пространство имён, например
http://example.ru/order. Это URI-идентификатор, по нему ничего не скачивается. - В редакторе пакета добавьте типы значений, затем типы объектов.
- У каждого типа объекта добавьте свойства и задайте им тип и границы.
- Добавьте корневое свойство пакета, если документ должен начинаться с конкретного элемента.
Обратная операция тоже есть: из пакета можно выгрузить схему командой Экспорт XML-схемы... и отдать её партнёру. В коде то же делает ФабрикаXDTO.ЭкспортСхемыXML(URI).
💡 URI пространства имён сразу выбирайте окончательный. Он попадает во все XML-документы. Смена URI потом ломает обмен у второй стороны.
Сериализация и десериализация: два пути
Здесь чаще всего путаются. У XDTO два разных механизма, и решают они разные задачи.
| Механизм | Что превращает в XML | Когда применять |
|---|---|---|
ФабрикаXDTO + ваш пакет | ОбъектXDTO, собранный по вашей схеме | Обмен с внешней системой по согласованному формату |
СериализаторXDTO | Объекты самой 1С: справочники, документы, ссылки, структуры, таблицы | Перенос данных между базами 1С с одинаковыми метаданными |
СериализаторXDTO
Сериализатор работает со служебными пакетами платформы, ваш пакет ему не нужен. Он пишет объект конфигурации целиком, со всеми реквизитами и табличными частями.
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
СериализаторXDTO.ЗаписатьXML(Запись, СсылкаНаДокумент.ПолучитьОбъект(), НазначениеТипаXML.Явное);
ТекстXML = Запись.Закрыть();
Обратно:
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстXML);
Объект = СериализаторXDTO.ПрочитатьXML(Чтение);
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();
НазначениеТипаXML.Явное добавляет в XML атрибут xsi:type. Без него при чтении платформа может не понять, какой тип восстанавливать.
Глобальные функции ЗаписатьXML() и ПрочитатьXML() — короткая запись того же сериализатора.
У сериализатора есть и промежуточный шаг: ЗаписатьXDTO() превращает значение в ОбъектXDTO, а ПрочитатьXDTO() делает обратное. Это удобно, когда объект 1С надо вложить в свой XDTO-объект как свойство.
Фабрика и свой пакет
Фабрика читает и пишет XML по типам вашего пакета. Структура документа диктуется схемой, а не метаданными 1С. Этим путём делают обмен с сайтами, банками, маркетплейсами, госсистемами и любыми не-1С системами.
Объекты XDTO в коде: создание, заполнение, проверка
Возьмём пакет с пространством имён http://example.ru/order. В нём тип Order со свойствами Number, Date и списком Item (верхняя граница -1). Тип строки OrderItem содержит Sku и Qty.
Собрать и записать
URI = "http://example.ru/order";
ТипЗаказ = ФабрикаXDTO.Тип(URI, "Order");
ТипПозиция = ФабрикаXDTO.Тип(URI, "OrderItem");
Заказ = ФабрикаXDTO.Создать(ТипЗаказ);
Заказ.Number = "А-125";
Заказ.Date = ТекущаяДатаСеанса();
Для Каждого Строка Из Товары Цикл
Позиция = ФабрикаXDTO.Создать(ТипПозиция);
Позиция.Sku = Строка.Артикул;
Позиция.Qty = Строка.Количество;
Заказ.Item.Добавить(Позиция);
КонецЦикла;
Заказ.Проверить(); // исключение, если объект не соответствует схеме
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
ФабрикаXDTO.ЗаписатьXML(Запись, Заказ, "Order", URI);
ТекстXML = Запись.Закрыть();
Метод Проверить() ловит пропущенные обязательные свойства и значения, которые не проходят фасеты. Вызывайте его до отправки: ошибку лучше увидеть у себя, чем в ответе партнёра.
Прочитать входящий XML
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ТекстXML);
Заказ = ФабрикаXDTO.ПрочитатьXML(Чтение, ФабрикаXDTO.Тип(URI, "Order"));
Для Каждого Позиция Из Заказ.Item Цикл
// Позиция.Sku, Позиция.Qty уже приведены к типам из схемы
КонецЦикла;
Полезные приёмы
- 🔍
Объект.Свойства()возвращает описание свойств типа. Так можно обойти объект, не зная заранее его структуру. - 📝 Обращение через точку работает, если имя свойства — допустимый идентификатор. Для имён с дефисом используйте
Объект.Получить("order-id")иОбъект.Установить("order-id", Значение). - 🏷 Префиксы пространств имён в выходном XML задаются методом
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен(Префикс, URI). Без него платформа сгенерирует служебные префиксы — XML будет корректным, но некоторые партнёры такой не принимают. - 🧪 Если XSD приходит в рантайме и в конфигурацию его не добавить, соберите отдельную фабрику: схему читают через
ПостроительСхемXML, кладут вНаборСхемXMLи передают в конструкторНовый ФабрикаXDTO(НаборСхем).
Типы данных XDTO и передача ссылок
Простые типы XML Schema платформа приводит к своим автоматически.
| Тип XML Schema | Тип в 1С |
|---|---|
xs:string | Строка |
xs:decimal, xs:int, xs:long | Число |
xs:dateTime, xs:date | Дата |
xs:boolean | Булево |
xs:base64Binary | ДвоичныеДанные |
Если в схеме свойство помечено как допускающее пустое значение (nillable), в XML оно запишется с xsi:nil="true". Это не то же самое, что отсутствие свойства. Партнёр может различать «поле не передано» и «поле передано пустым», поэтому договоритесь заранее, что значит каждое.
Как сериализуются ссылки
Ссылка на справочник или документ при записи через СериализаторXDTO превращается в строку с уникальным идентификатором (GUID). Типы объектов базы лежат в служебном пространстве имён http://v8.1c.ru/8.1/data/enterprise/current-config и называются по шаблону CatalogRef.Номенклатура, DocumentObject.РеализацияТоваровУслуг.
При чтении сериализатор восстанавливает ссылку по GUID. Наличие объекта в базе при этом не проверяется. Если такого элемента нет, вы получите битую ссылку, которая в формах показывается как «Объект не найден».
Отсюда правило: при переносе данных между базами сначала загружайте справочники, потом документы. Или проверяйте ссылку после чтения и создавайте недостающий элемент с тем же идентификатором через ПолучитьСсылку(Новый УникальныйИдентификатор(Строка)) и УстановитьСсылкуНового().
В обмене с внешней системой GUID 1С обычно бесполезен. Там передают артикул, ИНН или внешний код, а ссылку ищут на своей стороне. Свой пакет XDTO описывает именно такие поля, а не ссылки.
XDTO в веб-сервисах и HTTP-запросах
Веб-сервисы конфигурации
Объект метаданных «Web-сервис» в свойствах содержит список XDTO-пакетов. Типы параметров и возвращаемых значений операций выбираются из этих пакетов. При публикации платформа сама формирует WSDL на их основе.
Внутри модуля операции вы работаете с готовыми ОбъектXDTO: параметр пришёл уже разобранным, ответ собираете через ФабрикаXDTO.Создать() и возвращаете из функции.
Вызов чужого SOAP-сервиса идёт через прокси:
Определения = Новый WSОпределения("https://partner.example.ru/ws/orders?wsdl", Логин, Пароль);
Прокси = Новый WSПрокси(Определения, "http://partner.example.ru/orders", "OrdersService", "OrdersServiceSoap");
Прокси.Пользователь = Логин;
Прокси.Пароль = Пароль;
Фабрика = Прокси.ФабрикаXDTO; // типы из WSDL партнёра
Запрос = Фабрика.Создать(Фабрика.Тип("http://partner.example.ru/orders", "GetOrderRequest"));
Здесь важная деталь: у прокси своя фабрика, собранная из WSDL. Типы партнёра берут из Прокси.ФабрикаXDTO, а не из глобальной ФабрикаXDTO. Пакет в конфигурацию добавлять не нужно.
Пространство имён, имя сервиса и точки подключения смотрите в самом WSDL: атрибут targetNamespace, элементы service и port.
HTTP-запросы и REST с XML
Если партнёр принимает XML через обычный POST, XDTO сериализует тело, а транспорт берёт на себя HTTPСоединение:
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
ФабрикаXDTO.ЗаписатьXML(Запись, Заказ, "Order", URI);
HTTPЗапрос = Новый HTTPЗапрос("/api/orders");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/xml; charset=utf-8");
HTTPЗапрос.УстановитьТелоИзСтроки(Запись.Закрыть(), КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Соединение = Новый HTTPСоединение("partner.example.ru", 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL);
Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
Ответ разбирают так же, как входящий файл: ЧтениеXML.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку()), затем ФабрикаXDTO.ПрочитатьXML() с нужным типом.
В HTTP-сервисах конфигурации пакеты XDTO к сервису не привязываются. Тело запроса вы читаете сами и передаёте в фабрику.
Для JSON у сериализатора есть методы ЗаписатьJSON() и ПрочитатьJSON() (платформа 8.3.6 и выше). Они пишут объекты 1С в собственном формате платформы. Для обмена с внешним REST API JSON обычно собирают через ЗаписьJSON и структуры, без XDTO.
Как ускорить обмен через XDTO
XDTO удобен, но не бесплатен. На больших объёмах узкие места видны быстро.
| Проблема | Что делать |
|---|---|
| Фабрика из XSD создаётся при каждом вызове | Создайте один раз и держите в переменной модуля или включите пакет в конфигурацию |
| Файл на сотни мегабайт читается целиком | Читайте потоком: ЧтениеXML + ПрочитатьXML() по одному элементу |
| Тип ищется в цикле | Получите ФабрикаXDTO.Тип() один раз до цикла |
| Выгрузка объектов целиком сериализатором | Для внешних систем пишите только нужные поля своим пакетом |
Проверить() на каждом мелком объекте | Проверяйте корневой объект перед отправкой: проверка идёт по вложенным |
Потоковое чтение большого файла
Приём: идём по файлу обычным ЧтениеXML, а когда курсор встаёт на начало нужного элемента, отдаём его фабрике. Фабрика читает только этот элемент, память не растёт.
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяФайла);
ТипПозиция = ФабрикаXDTO.Тип(URI, "OrderItem");
ЕстьДанные = Чтение.Прочитать();
Пока ЕстьДанные Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.ЛокальноеИмя = "Item" Тогда
Позиция = ФабрикаXDTO.ПрочитатьXML(Чтение, ТипПозиция); // курсор уже за элементом
ОбработатьПозицию(Позиция);
Иначе
ЕстьДанные = Чтение.Прочитать();
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
После ПрочитатьXML() читатель уже стоит за прочитанным элементом. Поэтому Прочитать() вызывается только в ветке Иначе — так соседний элемент не пропустится.
💡 Записывать в базу удобнее порциями внутри транзакции, например по 500–1000 объектов. Одна транзакция на весь файл держит блокировки слишком долго.