Пример рекурсивной выгрузки иерархической структуры в XDTO

Решил реализовать иерархию в пакете XDTO и выгрузить ее рекурсивно. Задача оказалась нетривиальной, хотя и весьма простой. Изысканиями решил поделиться с народом, чтобы не пропадало народное добро.

Что такое XDTO?

Ну, полагаю, тут мало людей, кто не знает, что это такое. Но т.к. я постоянно вижу выгрузку в тот же CML, реализованную от незнания через обычную запись XML, куда гонятся узлы и атрибуты, то я думаю, что XDTO просто стращно звучит для многих. Упростим выражение до примитивов, чтобы и ежу было понятно.

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

Чтобы абстрагироваться от всей этой наносной терминологической мути, давайте тупо создадим пакет XDTO, замутим в нем иерархию и заставим ее выгружаться в XML-файл. И никаких дебрей терминологии — пусть терминологи идут рыть канавы — больше пользы)))

1. Создадим пакет XDTO

Для этого открываем конфигуратор и жамкаем правой кнопкой на XDTO-пакеты (в общих). Дальше нажимаем «Добавить»:

Да, вот так все просто. Кстати, пакет можно импортировать из файла XSD. Дальше надо прописать пакету какое-нибудь имя и это долбанное «URI пространства имен» (это любые символы, какие вам заблагорассудятся, но они потом будут использованы, так что креативчика поубавьте, чтобы не писать три страницы). Наш пакет пустой — он предмет простой (с). Давайте усложним — добавим Тип значений и пару Типов объектов:

Я, как видите, занялся идеей выгружать иерархическую структуру предприятия (кстати, ее потом сюда — в XDTO-пакет — можно будет загрузить и обойти). Поэтому я добавил Тип «УникальныйИдентификатор» и два Типа объектов — Классификатор и Подразделение. В классификаторе я определяю первичный контейнер для данных, а подразделение будет субконтейнером. В принципе, полагаю, можно обойтись и одним Подразделением, но так обычно не бывает и иерархический элемент воткнут в какой-то внешний контейнер.

Для Классификатора и Подразделения и создаю три свойства: Ид (с типом «УникальныйИдентификатор», который определен в пакете), Наименование и Подразделения с субконтейнером «Подразделение». Для свойства «Подразделение» я указываю тип «Подразделение» и прописываю минимальное и максимальное количество (0 и -1 соответственно). Это нужно для того, чтобы мы, во-первых, могли указать, что у данного подразделения нет подчиненных, а, во-вторых, могли бы указать любое количество подчиненных подразделений.

Так, с пакетом все.

2. Напишем код выгрузки

Создали пакеты, сохранили конфигурацию и создали внешнюю обработочку, да? Тогда давайте приступим к наполнению ее кодом.

Для того, чтобы получить наш пакет и начать с ним работать, нам надо получить какой-либо тип и создать на его основе XDTO-объект. В синтаксис-помощнике нет ни одного примера, поэтому тут придется показывать. Первая часть марлезонского балета выглядит как-то так:

Пакет = ФабрикаXDTO.Пакеты.Получить("sample-my-package");

Классификатор = ФабрикаXDTO.Создать(Пакет.Получить("Классификатор"));
Классификатор.Ид = ""+ (Новый УникальныйИдентификатор);
Классификатор.Наименование = "Организационная структура";

Видите, как просто! Получаем пакет из базовой фаблики по имени URI, чтоб ему пусто было. Никак не могу понять, кто эту хрень придумал, ну да ладно — не в этом суть. Главное — мы получили пакет, из которого теперь можем дергать свойства и создавать объекты XDTO. Дальше в коде мы получили на основании типа объекта «Классификатор» соответствующий объект XDTO. Дальше заполняем поля. А вот с иерархией все сложнее. Для простого случая можно сделать так:

Подразделения = ФабрикаXDTO.Создать(Пакет.Получить("Классификатор").Свойства.Получить("Подразделения").Тип);
Подразделение = ФабрикаXDTO.Создать(Пакет.Получить("Подразделение"));
Подразделение.Ид = ""+Выборка.УИН;
Подразделение.Наименование = Выборка.Наименование;
Подразделения.Подразделение.Добавить(Подразделение);

Тут мы добавили первый элемент в список подразделений. Чтобы подразделения попали в выгрузку, нам надо поместить объект «Подразделения» в Классификатор:

Классификатор.Подразделения = Подразделения;

В итоге мы получим только такой вот файл, если выгрузим это в XML:

<?xml version="1.0" encoding="UTF-8"?>
<Классификатор xmlns="sample-my-package" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Ид>1ea4a653-d292-4769-8ef7-91db4293f9b8</Ид>
<Наименование>Организационная структура</Наименование>
<Подразделения>
<Подразделение>
<Ид>6f87e821-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Бухгалтерия</Наименование>
</Подразделение>
</Подразделения>
</Классификатор>

Для того, чтобы все отработало без лишних действий, напишем рекурсивную функцию, которая будет возвращать контейнер «Подразделения» Каталога:

Классификатор.Подразделения = ВыгрузитьРекурсивно(Пакет, "СтруктураПредприятия");

Далее сама функция:

Функция ВыгрузитьРекурсивно(Пакет, Справочник, Родитель = Неопределено)

Если Родитель = Неопределено Тогда
Уровень = ФабрикаXDTO.Создать(Пакет.Получить("Классификатор").Свойства.Получить("Подразделения").Тип);
Иначе
Уровень = ФабрикаXDTO.Создать(Пакет.Получить("Подразделение").Свойства.Получить("Подразделения").Тип);
КонецЕсли;
ЕстьОбъекты = Ложь;

Для Каждого Выборка ИЗ ПолучитьМассивЗначений(Справочник, Родитель) Цикл
ЕстьОбъекты = Истина;
Подразделение = ФабрикаXDTO.Создать(Пакет.Получить("Подразделение"));
Подразделение.Ид = ""+Выборка.УИН;
Подразделение.Наименование = Выборка.Наименование;
ПодразделенияУровня = ВыгрузитьРекурсивно(Пакет, Справочник, Выборка.Ссылка);
Если НЕ ПодразделенияУровня = Неопределено Тогда
Подразделение.Подразделения = ПодразделенияУровня;
КонецЕсли;
Уровень.Подразделение.Добавить(Подразделение);
КонецЦикла;

Возврат ?(ЕстьОбъекты, Уровень, Неопределено);
КонецФункции

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

В итоге вот такой файлик получился при выгрузке из демоторговли:

<?xml version="1.0" encoding="UTF-8"?>
<Классификатор xmlns="sample-my-package" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Ид>1ea4a653-d292-4769-8ef7-91db4293f9b8</Ид>
<Наименование>Организационная структура</Наименование>
<Подразделения>
<Подразделение>
<Ид>6f87e821-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Бухгалтерия</Наименование>
</Подразделение>
<Подразделение>
<Ид>c37c8fa9-7ae7-11df-b33a-0011955cba6b</Ид>
<Наименование>Касса</Наименование>
</Подразделение>
<Подразделение>
<Ид>163cab6d-35ae-11e0-aefc-0015e9b8c48d</Ид>
<Наименование>Ларек "Розница"</Наименование>
</Подразделение>
<Подразделение>
<Ид>93961168-ed19-11e2-802e-0015e9b8c48d</Ид>
<Наименование>Магазин "Бытовая техника"</Наименование>
</Подразделение>
<Подразделение>
<Ид>163cab59-35ae-11e0-aefc-0015e9b8c48d</Ид>
<Наименование>Магазин "Продукты"</Наименование>
</Подразделение>
<Подразделение>
<Ид>163cab5a-35ae-11e0-aefc-0015e9b8c48d</Ид>
<Наименование>Магазин "Электротовары"</Наименование>
</Подразделение>
<Подразделение>
<Ид>a4212b47-730a-11df-b338-0011955cba6b</Ид>
<Наименование>Магазины (НТТ)</Наименование>
<Подразделения>
<Подразделение>
<Ид>163cab6d-35ae-11e0-aefc-0015e9b8c48d</Ид>
<Наименование>Ларек "Розница"</Наименование>
</Подразделение>
<Подразделение>
<Ид>93961168-ed19-11e2-802e-0015e9b8c48d</Ид>
<Наименование>Магазин "Бытовая техника"</Наименование>
</Подразделение>
<Подразделение>
<Ид>163cab59-35ae-11e0-aefc-0015e9b8c48d</Ид>
<Наименование>Магазин "Продукты"</Наименование>
</Подразделение>
<Подразделение>
<Ид>163cab5a-35ae-11e0-aefc-0015e9b8c48d</Ид>
<Наименование>Магазин "Электротовары"</Наименование>
</Подразделение>
</Подразделения>
</Подразделение>
<Подразделение>
<Ид>9e5ddc14-175a-11e2-bfa2-0015e9b8c48d</Ид>
<Наименование>Отдел доставки</Наименование>
</Подразделение>
<Подразделение>
<Ид>4d9d0772-7ab1-11df-b33a-0011955cba6b</Ид>
<Наименование>Отдел закупок</Наименование>
</Подразделение>
<Подразделение>
<Ид>51ed67e2-7220-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел логистики</Наименование>
</Подразделение>
<Подразделение>
<Ид>51ed67a5-7220-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел маркетинга</Наименование>
</Подразделение>
<Подразделение>
<Ид>be8a7643-f8df-11e2-802f-0015e9b8c48d</Ид>
<Наименование>Отдел оптовых продаж</Наименование>
</Подразделение>
<Подразделение>
<Ид>51ed67e1-7220-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел продаж</Наименование>
<Подразделения>
<Подразделение>
<Ид>be8a7643-f8df-11e2-802f-0015e9b8c48d</Ид>
<Наименование>Отдел оптовых продаж</Наименование>
</Подразделение>
<Подразделение>
<Ид>6f87e81f-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел работы с дилерами</Наименование>
</Подразделение>
<Подразделение>
<Ид>6f87e820-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел работы с дистрибьюторами</Наименование>
</Подразделение>
<Подразделение>
<Ид>be8a7644-f8df-11e2-802f-0015e9b8c48d</Ид>
<Наименование>Отдел розничных продаж</Наименование>
</Подразделение>
<Подразделение>
<Ид>49278eed-7ef4-11df-866e-00d0d0817f92</Ид>
<Наименование>Торговые представители</Наименование>
</Подразделение>
</Подразделения>
</Подразделение>
<Подразделение>
<Ид>6f87e81f-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел работы с дилерами</Наименование>
</Подразделение>
<Подразделение>
<Ид>6f87e820-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Отдел работы с дистрибьюторами</Наименование>
</Подразделение>
<Подразделение>
<Ид>be8a7644-f8df-11e2-802f-0015e9b8c48d</Ид>
<Наименование>Отдел розничных продаж</Наименование>
</Подразделение>
<Подразделение>
<Ид>6f87e81d-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Сервисная служба</Наименование>
</Подразделение>
<Подразделение>
<Ид>1418c674-7307-11df-b338-0011955cba6b</Ид>
<Наименование>Склад мебели</Наименование>
</Подразделение>
<Подразделение>
<Ид>6f87e81e-722c-11df-b336-0011955cba6b</Ид>
<Наименование>Склады</Наименование>
<Подразделения>
<Подразделение>
<Ид>1418c674-7307-11df-b338-0011955cba6b</Ид>
<Наименование>Склад мебели</Наименование>
</Подразделение>
<Подразделение>
<Ид>1418c675-7307-11df-b338-0011955cba6b</Ид>
<Наименование>Торговый зал</Наименование>
</Подразделение>
<Подразделение>
<Ид>1418c673-7307-11df-b338-0011955cba6b</Ид>
<Наименование>Центральный склад</Наименование>
</Подразделение>
</Подразделения>
</Подразделение>
<Подразделение>
<Ид>49278eed-7ef4-11df-866e-00d0d0817f92</Ид>
<Наименование>Торговые представители</Наименование>
</Подразделение>
<Подразделение>
<Ид>1418c675-7307-11df-b338-0011955cba6b</Ид>
<Наименование>Торговый зал</Наименование>
</Подразделение>
<Подразделение>
<Ид>1418c673-7307-11df-b338-0011955cba6b</Ид>
<Наименование>Центральный склад</Наименование>
</Подразделение>
</Подразделения>
</Классификатор>

Вообще, ничего не жалко для народа — выкладываю свою наработку за недорого, если кто все же не понял, что и как.

2 Comments

  1. hulio

    Не понял, для чего вы создали тип УникальныйИдентификатор? Все равно вы айдишники в строку преобразуете.

    Еще у вас на выходе неправильная XML-ка получилась — подразделения нижних уровней присутствуют и на верхнем уровне тоже. Например, подразделение «Склады» и содержит вложенные подразделения «Центральный склад», «Склад мебели» и т.д., а далее эти же подразделения идут сами по себе, без указания вложенности:

    <Подразделение>

    <Ид>6f87e81e-722c-11df-b336-0011955cba6b</Ид>

    <Наименование>Склады</Наименование>

    <Подразделения>

    <Подразделение>

    <Ид>1418c674-7307-11df-b338-0011955cba6b</Ид>

    <Наименование>Склад мебели</Наименование>

    </Подразделение>

    <Подразделение>

    <Ид>1418c675-7307-11df-b338-0011955cba6b</Ид>

    <Наименование>Торговый зал</Наименование>


    </Подразделение>

    <Подразделение>

    <Ид>1418c673-7307-11df-b338-0011955cba6b</Ид>

    <Наименование>Центральный склад</Наименование>

    </Подразделение>

    </Подразделения>

    </Подразделение>

    <Подразделение>

    <Ид>49278eed-7ef4-11df-866e-00d0d0817f92</Ид>

    <Наименование>Торговые представители</Наименование>

    </Подразделение>

    <Подразделение>

    <Ид>1418c675-7307-11df-b338-0011955cba6b</Ид>

    <Наименование>Торговый зал</Наименование>


    </Подразделение>

    В таком случае было бы проще не упаковывать вложенные подразделения в отдельный контейнер, а добавить свойство «ИДРодителя»

    Reply
  2. starik-2005

    (1) hulio, замечательно, что Вы заметили.

    1. По вопросу у УИН, то это как пример того, как можно определять типы данных в XDTO-пакете.

    2. По поводу файла, то действительно вывелось все и сразу. Причиной тому — передача «Неопределено» в функцию, в которой выбирается текущий уровень через выборку справочника. Это легко исправляется.

    3. По поводу ИД родителя, то это не удовлетворяет условию разбираемой тут задачи, а именно — выгрузка иерархического справочника с помощью рекурсии.

    Надеюсь, я смог ответить на все Ваши вопросы.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *