XDTO - это просто, часть 2
Обновление 28.01.2013:
Опубликована очередная статья: //infostart.ru/public/171019/
В предыдущей статье было дано небольшое введение в механизм XDTO, предлагаемый платформой 1С. Это вторая часть серии про XDTO, где предлагается рассмотреть основные моменты создания модели типов XDTO с помощью схем XML, т.к. понятие модели типов фундаментально для XDTO, и без понимания того, какие типы бывают сложно двигаться дальше.
Стоит также отметить, что данная статья, по сути, является прямым продолжением первой статьи, т.е. все, что будет написано ниже должно было быть включено в первую статью, но она, неожиданно для меня, получилась довольно большой и ее пришлось разбить на две. Таким образом, обе статьи следует считать одной, тема здесь и там рассматривается одна и та же - самые основы-основ XDTO с минимумом "внутренностей". Более детальные обзоры требуют понимания фундамента, поэтому, данная вводная часть, хоть и не так интересна, но, тем не менее, просто обязана быть. Еще раз, хочу обратить внимание - если материал статьи Вам и так известен, пролистайте ее, а в комментариях напишите те вопросы, которые нужно осветить подробно в будущих статьях.
Liquid XML Studio
Как я уже говорил, для разработки схем я пользуюсь Liquid XML Studio, а стало быть примеры схем будут приведены для этой программы. Разумеется, никто не запрещает создавать схемы любыми другими редакторами, например XML Spy или Oxygen. Старый добрый Блокнот тоже никто не отменял ;). Да, забыл! В 1С есть встроенный редактор типов XDTO. Он чуть лучше, чем блокнот, поэтому, запросто можно использовать его для небольших схем.
Какие бывают типы?
Как я уже говорил, фундаментальным понятием для XDTO является модель типов, т.е. совокупность всех прикладных (полезных бизнес-логике) типов данных. Прикладных типов может быть много, но по принципу построения все они похожи.
Во-первых, типы делятся на простые (simple) и составные (complex). Составные типы могут иметь несколько моделей содержимого, но XDTO поддерживает только простые последовательности свойств - так называемые sequence. Вдаваться в особенности моделей содержимого не будем. Sequence - это просто перечень свойств, как реквизиты справочника, например. Остальные в рамках данной темы нам не интересны.
Простые типы представляют данные, которые можно выразить одним строковым значением, например даты, строки и числа. Составные, как следует из названия, это типы содержащие несколько значений.
Стандарты XML представляют большое количество базовых типов данных. Все их перечислять нет смысла, некоторые будут в примерах, при необходимости, их можно найти в стандартах w3c, а также в гугле.
Типы могут наследовать друг-друга, так же, как в любимом всеми ООП. Например, “ФизЛицо” с атрибутами имени, номера паспорта и адресом может наследоваться типами “Сотрудник” и “Клиент”. На наследовании останавливаться не будем, думаю, все знают, что это такое. Кроме того, свойства в составных типах данных могут иметь так называемую “повторяемость”, т.е. встречаться в документе более 1 раза. По-простому, это называется список.
Как тип данных может выглядеть в XML?
Рассмотрим небольшой тип данных, который описывает одно сообщение в сетевом чате типа аськи:
У сообщения есть уникальный номер, дата-время его поступления на сервер и, собственно, тело сообщения. Здесь интересный момент. Составной тип данных может выглядеть в XML разными способами, но логически означать одно и тоже. В XDTO, как в средстве “логической” обработки данных, все типы будут обрабатываться из языка 1С одинаково, но физически в потоке XML они могут быть представлены разными способами.
Способов представления всего три: Элемент, Атрибут и Текст. Все очень просто - элементы пишутся в XML, как элементы, атрибуты - как атрибуты, а текст - как текст (логично, правда?). Напомним, как выглядят эти вещи в синтаксисе XML:
Обратите внимание, свойства number и date представлены в виде атрибутов, а тело сообщения представлено в виде текста. Если вы разрабатываете свой тип данных, то способы размещения выбирать вам. Атрибут или Элемент - можно холиварить, но в конечном итоге - это дело вкуса.
Никто не запрещает оформить наш тип данных вот так:
Ровно те же самые данные, но выглядят иначе. Для XDTO это важно, т.к. при чтении потока XML, платформа должна понимать, как отобразить XML на бизнес-объект XDTO. Об особенностях настройки этого момента в XDTO расскажу ниже.
Конструирование типа в Liquid
Рассмотрим создание указанной модели типов в Liquid. Перво-наперво, создадим чистую схему и зададим ей уникальное пространство имен (вспоминаем первую статью).
В корневом элементе схемы щелкнем правой кнопкой и выберем Add child->Complex type.
Будет добавлен новый тип. Назовем его Message, а в качестве базового укажем string. Базовый тип означает, что наш новый тип в конечном итоге является строкой. Это позволит помещать тело сообщения в текстовый узел XML (см. первую картинку, выше).
Теперь, добавим 2 атрибута - номер и дату сообщения. Правой кнопкой щелкаем на типе Message, выбираем Add child->Attribute. Задаем имя атрибута и тип его значения. Для number - это будет тип decimal (число), для date - dateTime.
Тип Message готов, теперь нужно создать объект пакета сообщений MessagePackage, как в приведенном примере.
Добавляем новый составной тип (корневой элемент, Add child->Complex type), задаем ему имя типа MessagePackage. Базовый тип не указываем.
Теперь еще один момент. Чтобы составной тип мог содержать другие элементы, нужно указать ему модель содержимого. Как я уже говорил, XDTO поддерживает только Sequence. Добавляем в MessagePackage дочерний элемент (правой кнопкой, Add child) и выбираем Sequence. Появится такая серая кракозябла, в которую впоследствии и надо добавлять дочерние элементы.
Щелкнем на этой кракозябле, укажем Add Child - > Element. Для элемента зададим имя (любое), а главное - тип значения - наш тип Message.
В результате получится такая картинка:
Теперь, нам надо указать, что Message может повторяться несколько раз внутри MessagePackage. Для этого, в контекстном меню элемента Message (того, который красненький, ведь это именно он должен повторяться) выберем пункт Cardinality.
Данное свойство позволяет указать особенности повторяемости. Как правило, элементы повторяются либо неограниченное число раз (1..unbounded), либо являются необязательными (т.е. повторяются от 0 до 1 раз). Если cardinality не указан, что считается, что элемент обязательный и присутствует только один раз.
Для особо тяжелых случаев в свойствах элемента можно настроить точную повторяемость с помощью Min Occurs и Max Occurs. Например, можно указать, что элемент должен встречаться не менее 2-х и не более 10-раз.
Сообщений в нашем пакете может быть одно и более. Указываем Cardinality “1..unbounded”. Теперь элемент Message может повторяться более в пакете более 1 раза. В XDTO такой элемнет будет не просто свойством, а СпискомXDTO.
Обратите внимание, тип Message унаследован от string, а это значит он ЯВЛЯЕТСЯ строкой, просто у этой строки есть 2 дополнительных свойства. Кроме того, само тело сообщения не имеет какого-то имени, к которому можно обратится через точку.
Здесь вступает в игру противоречие между концепцией хранения данных в XML и объектной моделью языка 1С. Например, в общем случае, мы можем объявить тип Message, и из языка 1С задать значение number одной строкой кода:
message.number = 12;
Но что делать, если в схеме нет имени свойства (как в нашем примере с текстом), или свойство называется “message-id”? XML не запрещает дефисы в именах, а вот компилятору 1С вряд ли понравится переменная с дефисом в имени. Мне кажется, это одна из причин, по которой в 1С разделили понятие “Схемы” и понятие “Модели данных”. XML не может однозначно отображаться на объектную модель языка. Требуются дополнительные настройки, а значит, термины, понятия и объекты...
Вернемся в Конфигуратор
Итак, представим, что в корпоративной сети нашей фирмы есть внутренний чат, который называется Corporative Messaging System, сокращенно CMS. Протокол обмена сообщениями - приведенная выше схема. По сети гуляют пакеты сообщений в виде приведенных фрагментов XML. Давайте напишем для этой системы генератор спама на языке 1С.
Сама CMS написана не на 1С, а на Турбо Бейсике 1.0 (чтобы было ясно, что XDTO применимо не только к обмену 1С->1С). При помещении файла XML в специальный каталог он подхватывается сервером CMS и транслируется дальше по сети. Архитектура не ахти какая, но это же Бейсик 1.0, так что не придираемся :)
Импорт схемы в конфигуратор
Первое, что мы должны сделать, это выяснить к какому пространству имен относятся типы почтовой системы. В Liquid надо выбрать корневой элемент схемы и открыть окно свойств из контекстного меню. В списке свойств ищем “targetNamespace”. Допустим в нашем примере пространство имен называется “urn:my-company/cms”
Теперь, нам надо загрузить схему в конфигуратор. Это мы делали в прошлой статье: щелкаем правой кнопкой на ветке ПакетыXDTO и выбираем “Импорт схемы XML”.
Теперь, откроем на редактирование новый Пакет XDTO и посмотрим, что у него внутри.
Ага, все знакомо, только что это за __content в типе Message? Это как раз то, что платформа предлагает в качестве свойства для текстового содержимого объекта. Модель XDTO предлагает “заменители” для тех имен XML-схемы, которые не могут использоваться в качестве имен переменных в языке 1С. Давайте откроем окно свойств для элемента __content в типе Message.
Обратите внимание, на пункт “Локальное имя”. Он означает то, как свойство представлено в самом файле XML. Пункт “Имя” вверху - то, как объект выглядит в языке 1С. В идеале, они должны совпадать, но если вдруг они различаются, то здесь можно настроить трактовку подобных различий.
Также стоит обратить внимание на пункт “Форма”. Сейчас там стоит “Текст”, это значит, что значение расположено в текстовом узле. Например, если система CMS станет писать message.number не в атрибут, а в обычный элемент, то нам нужно будет поменять поле “Форма” и указать “Элемент”, чтобы новые файлы корректно обрабатывались. А если поле number еще и переименуется в message-id, то надо будет откорректировать “Локальное имя”. Таким образом, независимо (почти) от того, как данные оформлены внутри XML, мы можем настроить то, как они будут выглядеть в языке, а значит при изменении схемы XML нам не придется переписывать код.
Если нам не нравится писать Message.__content = “Hello”, то можно задать другое значение в поле “Имя”. При этом файл будет читаться без ошибок, опираясь на настройку "Форма" и "Локальное имя".
Что там насчет генератора спама?
Да, да. Я не забыл :) Создаем обработку, вешаем на нее большую кнопку и всю необходимую инфраструктуру настроек (текст спама, список адресатов и т.п.).
В самом интересном месте, там где должен формироваться XML документ, код будет выглядеть следующим образом:
ПакетСообщений = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("urn:my-company/cms"),"MessagePackage");
Для Каждого Адресат Из СписокАдресатов Цикл
Сообщение = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("urn:my-company/cms"),"Message");
Сообщение.number = Адресат.Номер;
Сообщение.date = ТекущаяДата();
Сообщение.__content = "Купите дубленку";
ПакетСообщений.Добавить(Сообщение);
Сообщение = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("urn:my-company/cms"),"Message");
Сообщение.number = Адресат.Номер;
Сообщение.date = ТекущаяДата();
Сообщение.__content = "Уран недорого";
ПакетСообщений.Message.Добавить(Сообщение);
КонецЦикла;
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ФайлПочтовойСистемы);
ФабрикаXDTO.ЗаписатьXML(Запись, ПакетСообщений);
Запись.Закрыть();
На этом все. Файл со списком сообщений будет корректно сформирован в том месте, где мы указали.
А теперь, следите за руками!
В приведенном примере кода объект Сообщение создается 2 раза. Угадайте, что будет, если убрать вторую строчку “Сообщение = ФабрикаXDTO.Создать”? А будет вот что:
После добавления первого сообщения пакет сообщений содержит 1 элемент. После второго добавления в дерево будет добавлен еще раз тот же самый объект. Загадка: сколько объектов "Сообщение" будет записано в файл XML?
Будет записан только один объект. И этот эффект абсолютно нигде не документирован. Происходит следующее: объект XDTO представляет собой единый граф XML документа. Т.е. все вложенные элементы уникальны, и по объекту XDTO можно гулять вверх-вниз, определяя, какой XML записан выше или ниже объекта. Если мы возьмем объект Message, который уже помещен внутрь списка и присвоим его куда-то еще (например в другой список) то он удалится из первого списка. Объект уникален. Он не будет скопирован в другое место, он будет перемещен, а его связи перестроены.
Еще один пример. Есть тип “Должность” со свойствами “Название” и “Оклад”.
Записываем список сотрудников, где все они имеют должность “Рабочий”
ДолжностьРабочий = СоздатьДолжностьXDTO(); // Фабрика.Создать(.. ля-ля-ля);
СписокСотрудников = СоздатьСписокСотрудников(); // Фабрика.Создать(.. "НекийСписок")
Для Каждого Сотрудник Из Сотрудники Цикл
xdtoСотрудник = ФабрикаXDTO.Создать("namespace","Сотрудник");
xdtoСотрудник.Должность = ДолжностьРабочий; // Это всегда один и тот же объект
СписокСотрудников.Добавить(xdtoСотрудник);
КонецЦикла;
ФабрикаXDTO.Записать(ЗаписьXML, СписокСотрудников);
Объект “ДолжностьРабочий” будет гулять от сотрудника к сотруднику, и должность окажется назначенной только у последнего в списке. У всех остальных Должность будет пустой, т.к. она по очереди “перепрыгнула” от одного к другому.
В заключение
В моем редакторе получается 8 страниц. По-моему, для одной статьи даже много и нужно прерваться.
В этой статье мы рассмотрели то, как создаются XML схемы с помощью Liquid и как они отображаются в объекты XDTO. В других редакторах схем они создаются практически так же. Были рассмотрены разные способы задания объектов в XML, определились с терминами “Атрибут”, “Элемент”, “Текстовый узел”. Кроме того, в приведенном примере рассмотрен один нюанс, который с непривычки сложно понять. При присваивании объекта XDTO куда-либо в другом объекте, первый объект удаляется из своего старого места (если он имел таковое), и помещается в новое место. Нельзя один раз создать маленький повторяющийся объект (вроде "Должности"), а потом помещать его в несколько разных других объектов.
На этом предлагаю остановиться. Более подробно копать XDTO будем в одной из следующих статей.
В следующей серии:
Создание собственных Фабрик
Тонкости сериализации
Стандартная XDTO сериализация, с чем едят "СериализаторXDTO" и чем он отличается от "Фабрики".
Запись/чтение XDTO в поток XML, особенности anyType.