Как заполнить свойства объекта «Определение типа» в XDTO на примере CommerceML 2

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

При реализации интеграционных механизмов между системами 1С и внешними сайтами (например, интернет-магазинами на платформе 1С-Битрикс) разработчики регулярно сталкиваются с необходимостью выгрузки данных в формате CommerceML 2. Для решения этой задачи традиционно применяется встроенный механизм платформы — Фабрика XDTO и поставляемые схемы XSD (в частности, типовой пакет CML208).

Однако структура схемы таит в себе неочевидные элементы, такие как анонимные типы, которые в редакторе XDTO-пакета конфигуратора отображаются с пустым типом и описанием «Определение типа». Попытка заполнить такие свойства стандартными методами «в лоб» приводит к ошибке несоответствия типов. Проанализируем причину возникновения этой проблемы и разберем наиболее надежные и правильные способы ее решения.

Почему возникает ошибка при заполнении свойства «АдресноеПоле»?

Рассмотрим структуру объекта Адрес в схеме CommerceML. Данный объект содержит список АдресноеПоле, предназначенный для хранения составляющих адреса (таких как страна, регион, город, улица). Если открыть XDTO-пакет CML208 в конфигураторе 1С, мы увидим, что у свойства АдресноеПоле колонка «Тип» пуста. Вместо этого внутри свойства создается локальное «Определение типа» (так называемый анонимный inline-тип), содержащий свойства Тип и Значение.

Попытка получить тип этого поля через фабрику стандартным путем:

ТипАдресногоПоля = ФабрикаXDTO.Тип("urn:1C.ru:commerceml_2", "АдресноеПоле");

всегда будет возвращать значение Неопределено, поскольку данный тип не имеет собственного уникального имени в глобальном пространстве имен схемы. Платформа 1С не знает о существовании такого изолированного типа данных вне контекста родительского объекта, из-за чего разработчики часто получают ошибку «Несоответствие типов» при попытке сериализовать или добавить созданный элемент в список.

Решение №1: Получение анонимного типа из метаданных родителя (Рекомендуемый способ)

Выясним наиболее чистый и правильный с точки зрения архитектуры способ. Мы можем получить описание анонимного типа, обратившись напрямую к свойствам его родительского типа. Поскольку тип Адрес является глобальным и известным, мы можем прочитать его структуру и извлечь тип вложенного свойства АдресноеПоле без создания самого объекта.

Разберем этот процесс по шагам на примере кода:


// Шаг 1. Получаем глобальный тип "Адрес" из фабрики XDTO по URI пространства имен
ТипАдрес = ФабрикаXDTO.Тип("urn:1C.ru:commerceml_2", "Адрес");

// Шаг 2. Находим свойство "АдресноеПоле" в метаданных родителя и извлекаем его тип
ТипАдресногоПоля = ТипАдрес.Свойства.Получить("АдресноеПоле").Тип;

// Шаг 3. Теперь мы можем безопасно создавать объекты нужного анонимного типа
НовыйАдресXDTO = ФабрикаXDTO.Создать(ТипАдрес);
НовоеПолеXDTO = ФабрикаXDTO.Создать(ТипАдресногоПоля);

Данный способ является наиболее производительным и надежным, поскольку он исключает работу с «магическими» строками и не требует предварительного создания экземпляра родительского объекта для анализа его свойств.

Решение №2: Динамическое получение типа через «ВладеющееСвойство»

Рассмотрим альтернативный способ, который основывается на анализе метаданных уже созданного и инициализированного экземпляра объекта XDTO. Если у нас уже создан целевой объект Адрес, мы можем динамически обратиться к его списку АдресноеПоле и получить тип составляющих его элементов через специальное системное свойство ВладеющееСвойство.Тип.

Посмотрим на пример практической реализации этого подхода:


// Создаем экземпляр адреса стандартным способом
АдресТип = ФабрикаXDTO.Тип("urn:1C.ru:commerceml_2", "Адрес");
АдресXDTO = ФабрикаXDTO.Создать(АдресТип);

// Получаем тип анонимного элемента через метаданные списка уже существующего объекта
ТипАдресногоПоля = АдресXDTO.АдресноеПоле.ВладеющееСвойство.Тип;

// Создаем объект адресного поля на основе полученного типа
АдресноеПолеXDTO = ФабрикаXDTO.Создать(ТипАдресногоПоля);

Хотя этот вариант полностью рабочий, он заставляет разработчика совершать лишние действия в памяти (сначала инициализировать родительский объект, чтобы затем вытащить из него описание вложенных типов), что делает его менее изящным по сравнению с первым решением.

Критическая ошибка: Перезапись ссылок в оперативной памяти (Разбор бага)

При работе со списками XDTO начинающие разработчики часто совершают грубую ошибку, связанную с непониманием ссылочной модели данных в оперативной памяти 1С. Проанализируем типичный ошибочный код:


// ВНИМАНИЕ: Код содержит критическую логическую ошибку!
АдресноеПолеXDTO = ФабрикаXDTO.Создать(АдресXDTO.АдресноеПоле.ВладеющееСвойство.Тип);

// Пытаемся добавить Страну
АдресноеПолеXDTO.Тип = "Страна";
АдресноеПолеXDTO.Значение = "РОССИЯ";
АдресXDTO.АдресноеПоле.Добавить(АдресноеПолеXDTO);

// Пытаемся добавить Регион в тот же самый объект
АдресноеПолеXDTO.Тип = "Регион";
АдресноеПолеXDTO.Значение = "Краснодарский край";
АдресXDTO.АдресноеПоле.Добавить(АдресноеПолеXDTO);

Выясним причину некорректной работы этой конструкции. Переменная АдресноеПолеXDTO указывает на конкретную область в памяти, где был создан единственный экземпляр объекта. Когда мы присваиваем свойства «Страна» и добавляем элемент в список, в коллекцию помещается ссылка на этот объект. На следующем шаге мы меняем значение свойства Тип у того же самого объекта в памяти на «Регион» и снова добавляем его в список.

В результате при сериализации в XML-файл мы получим дублирующиеся данные — у нас дважды выгрузится «Регион», а «Страна» будет полностью стерта, так как коллекция содержит две ссылки на один и тот же измененный объект. Чтобы избежать этой проблемы, необходимо принудительно инициализировать новый экземпляр через ФабрикаXDTO.Создать() для каждого нового элемента списка.

Правильный пример заполнения адреса в формате CommerceML 2

Объединим полученные знания и напишем надежный, чистый алгоритм заполнения адреса контрагента для последующей выгрузки. Мы будем использовать правильный способ извлечения типов через метаданные и создавать новые объекты в цикле.


// 1. Инициализируем типы данных
ПространствоИмен = "urn:1C.ru:commerceml_2";
ТипАдрес = ФабрикаXDTO.Тип(ПространствоИмен, "Адрес");
ТипАдресногоПоля = ТипАдрес.Свойства.Получить("АдресноеПоле").Тип;

// 2. Создаем основной объект адреса
АдресXDTO = ФабрикаXDTO.Создать(ТипАдрес);
АдресXDTO.Представление = ТекСтрока.АдресПредставление;

// 3. Подготовим структуру данных для заполнения
АдресныеДанные = Новый Соответствие;
Если ЗначениеЗаполнено(ТекСтрока.АдресСтрана) Тогда
    АдресныеДанные.Вставить("Страна", ТекСтрока.АдресСтрана);
КонецЕсли;
Если ЗначениеЗаполнено(ТекСтрока.АдресРегион) Тогда
    АдресныеДанные.Вставить("Регион", ТекСтрока.АдресРегион);
КонецЕсли;
Если ЗначениеЗаполнено(ТекСтрока.АдресГород) Тогда
    АдресныеДанные.Вставить("Город", ТекСтрока.АдресГород);
КонецЕсли;

// 4. Заполняем список анонимных объектов в цикле
Для Каждого ТекущееПоле Из АдресныеДанные Цикл
    // КРИТИЧЕСКИ ВАЖНО: Создаем НОВЫЙ объект на каждой итерации цикла!
    НовоеПоле = ФабрикаXDTO.Создать(ТипАдресногоПоля);
    НовоеПоле.Тип = ТекущееПоле.Ключ;
    НовоеПоле.Значение = ТекущееПоле.Значение;
    
    // Добавляем созданный объект в список адреса
    АдресXDTO.АдресноеПоле.Добавить(НовоеПоле);
КонецЦикла;

Такой подход полностью гарантирует корректную структуру выгружаемого XML-файла и исключает потерю или случайное дублирование данных в оперативной памяти.

Полезные практики при работе с XDTO

При разработке сложных интеграционных механизмов на основе XDTO-пакетов рекомендуется придерживаться следующих правил:

  1. Всегда вызывайте метод валидации: Перед записью сформированного объекта в файл или отправкой на веб-сервис обязательно вызывайте метод ОбъектXDTO.Проверить(). Данный метод выполняет автоматическую валидацию данных на соответствие исходной схеме XSD (проверяет обязательность заполнения реквизитов, ограничения по типам и длине строк) и позволяет отловить ошибки еще на стороне 1С.
  2. Используйте механизмы БСП: Если вы ведете разработку в типовой конфигурации 1С, где интегрирована Библиотека стандартных подсистем (БСП), обратите внимание на общий модуль ОбменДаннымиXDTOСервер. Использование встроенной функции ОбъектXDTOИзДанныхXDTO() позволяет автоматически конвертировать обычные структуры и соответствия 1С в сложные древовидные структуры XDTO без необходимости вручную описывать создание каждого вложенного анонимного объекта.
← На главную