Как автоматически создать вложенные объекты XDTO по иерархии в 1С?

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

При работе с внешними веб-сервисами и обмене данными через формат XML программисты 1С часто сталкиваются с особенностью работы ФабрикаXDTO. Когда мы создаем объект верхнего уровня на основании типа из пакета XDTO, его свойства, имеющие сложные типы (другие объекты), не инициализируются автоматически. В итоге мы получаем объект, где все вложенные узлы имеют значение Неопределено. Это заставляет разработчика вручную прописывать создание каждого дочернего элемента, что крайне неудобно при глубокой вложенности схемы.

В этой статье мы подробно рассмотрим, почему так происходит, и разберем несколько стратегий автоматизации этого процесса: от ручного заполнения до универсальных рекурсивных функций.

Почему 1С не создает вложенные объекты автоматически?

Проанализируем логику работы платформы. Объект XDTO (XML Data Transfer Objects) строится строго по схеме XSD. В схеме для каждого свойства указаны квантификаторы — МинимальноеКоличество (minOccurs) и МаксимальноеКоличество (maxOccurs). Если бы 1С при создании корневого объекта автоматически создавала все вложенные свойства, это могло бы привести к бесконечной рекурсии (в случае циклических ссылок в схеме) или к созданию тысяч ненужных пустых объектов, если в схеме предусмотрены массивы (списки).

Поэтому по умолчанию ФабрикаXDTO.Создать() генерирует только "оболочку" объекта. Все, что находится внутри, программист должен инициализировать самостоятельно. Выясним, как сделать это максимально эффективно.

Способ 1. Создание объектов «снизу вверх» (Ручной метод)

Этот подход считается самым надежным и контролируемым. Его суть заключается в том, что мы сначала создаем самые "мелкие" объекты (листья дерева), а затем присваиваем их свойствам родительских объектов. Рассмотрим на примере, как это выглядит в коде:


// Получаем типы из фабрики
ТипАдрес = ФабрикаXDTO.Тип("http://myschema", "Address");
ТипКонтрагент = ФабрикаXDTO.Тип("http://myschema", "Counterparty");

// 1. Создаем вложенный объект (низ иерархии)
ОбъектАдрес = ФабрикаXDTO.Создать(ТипАдрес);
ОбъектАдрес.Город = "Москва";
ОбъектАдрес.Улица = "Ленина";

// 2. Создаем основной объект
ОбъектКонтрагент = ФабрикаXDTO.Создать(ТипКонтрагент);
ОбъектКонтрагент.Наименование = "ООО Ромашка";

// 3. Устанавливаем связь (помещаем вложенный объект в родительский)
ОбъектКонтрагент.ЮридическийАдрес = ОбъектАдрес;

Важный момент: Если свойство в схеме является списком (МаксимальноеКоличество > 1), то прямое присваивание не сработает. В этом случае нужно использовать метод Добавить() у коллекции XDTOList.

Способ 2. Рекурсивное создание через анализ метаданных типа

Если структура пакета очень большая, писать ручной код для каждого узла нерационально. Разберем, как написать универсальную функцию, которая будет "пробегать" по всем свойствам типа и автоматически создавать вложенные объекты, если они являются обязательными (НижняяГраница > 0).

Посмотрим на пример реализации такой функции:


Функция СоздатьОбъектXDTOСоВложенными(ТипXDTO)
    
    НовыйОбъект = ФабрикаXDTO.Создать(ТипXDTO);
    
    // Перебираем все свойства, описанные в типе XDTO
    Для Каждого Свойство Из ТипXDTO.Свойства Цикл
        
        // Проверяем, является ли свойство сложным объектом (не простым типом)
        Если Свойство.Тип.ТипКонтекста = ТипКонтекстаXDTO.Произвольный ИЛИ Свойство.Тип.Свойства.Количество() > 0 Тогда
            
            // Если свойство обязательное (НижняяГраница > 0) и не является списком
            Если Свойство.НижняяГраница > 0 И Свойство.МаксимальноеКоличество = 1 Тогда
                // Рекурсивно вызываем создание для вложенного типа
                НовыйОбъект[Свойство.Имя] = СоздатьОбъектXDTOСоВложенными(Свойство.Тип);
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат НовыйОбъект;
    
КонецФункции

Используя такую функцию, достаточно вызвать ее для корневого типа, и дерево объектов будет построено автоматически согласно правилам обязательности полей в XSD-схеме.

Способ 3. Использование промежуточных структур (Структура -> XDTO)

Часто программисту удобнее работать с привычными объектами 1С, такими как Структура и Массив. Мы можем подготовить данные в виде обычной структуры, а затем одной универсальной процедурой преобразовать ее в объект XDTO. Это позволяет отделить логику подготовки данных от технических особенностей работы ФабрикаXDTO.

Проанализируем алгоритм такого преобразования:

  1. Создаем Структуру, ключи которой совпадают с именами свойств в XDTO.
  2. Если свойство должно быть объектом — вкладываем в ключ еще одну Структуру.
  3. Если свойство должно быть списком — вкладываем Массив структур.
  4. Передаем эту "матрешку" в функцию заполнения, которая сопоставляет типы и выполняет ФабрикаXDTO.Создать().

Способ 4. Метод загрузки из XML-шаблона

Если структура объекта статична и очень сложна, можно пойти на хитрость. Создадим один раз "пустой" XML-файл, который содержит все необходимые теги и вложенности. Вместо программного конструирования мы можем просто прочитать этот шаблон.

Рассмотрим пошагово:

  1. Подготавливаем строку или файл с XML-кодом, где присутствуют все узлы, но значения тегов пусты.
  2. Используем ЧтениеXML для инициализации потока.
  3. Вызываем ФабрикаXDTO.Прочитать(ЧтениеXML, ТипXDTO).

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

Особенности работы со списками и типом AnyType

При автоматизации создания иерархии нужно учитывать два "подводных камня":

1. Списки (XDTOList). Свойства, у которых МаксимальноеКоличество не равно 1, ведут себя как коллекции. Вы не можете просто присвоить им созданный объект. Нужно вызвать Объект.ИмяСвойства.Добавить(НовыйВложенныйОбъект). В рекурсивных функциях это требует отдельной проверки свойства на признак коллекции.

2. Тип AnyType. Если в схеме указано, что свойство имеет тип AnyType, платформа не знает, какой именно объект там должен находиться. В этом случае ФабрикаXDTO.Создать() не сможет автоматически определить тип вложенного элемента. Вам придется явно указывать тип из пакета через ФабрикаXDTO.Тип(URIПространстваИмен, ИмяТипа) при создании такого свойства.

Подведем итог: Для простых задач достаточно ручного создания "снизу вверх". Для сложных и динамических интеграций лучше один раз написать качественную рекурсивную функцию или использовать механизм трансформации из структур 1С. Это сократит объем кода и сделает его более читаемым.

← На главную