Консалтинг и автоматизация в области управления
эффективностью банковского бизнеса

Журнал ВРМ World

Понимание сложных типов W3C Schema

Правда ли сложные типы W3C XML Schema настолько сложны для понимания, что нечего и пытаться их понять? Именно этой точки зрения придерживается Косьюк Коуэгучи (Kohsuke Kawaguchi); по крайней мере, именно так он пишет в своей последней статье на XML.com, заверяя, что для написания сложных типов не обязательно их понимать.

Однако зачем стремиться к написанию сложных типов, не понимая их, когда их вовсе не так уж сложно понять? Для понимания сложных типов в W3C Schemas нужно знать четыре вещи. Эти четыре момента понять совсем несложно. Посмотрим, что вы скажете об аргументах Коуэгучи после того, как ознакомитесь с ними.

Одним из важнейших, но наименее популярных аспектов W3C schemas является иерархия типов. Значение иерархии типов трудно переоценить. Почему? Потому что синтаксис для выражения типов в схемах точно следует иерархии типов.

XML Schema Part 2: Datatypes, Section 3 содержит наглядное изображение иерархии типов схемы.

Типы схемы образуют иерархию, так как все они происходят, напрямую или косвенно, из корневого типа. Корневой тип - это anyType. (anyType можно и в самом деле использовать в объявлении элемента - это предполагает какой угодно контент). Иерархия типов сначала разветвляется на две группы: простые типы и сложные типы. Здесь мы сталкиваемся с первыми двумя из четырех аспектов, необходимых нам для понимания сложных типов: первый, выведение является основой связи между типами в иерархии типов; и второй, первичное разветвление иерархии дает две группы - простые и сложные типы.

Разновидности выведения

Наследовать тип означает взять существующий тип, именуемый "базовый", и преобразовать его каким-либо образом для получения нового типа. Существует четыре разновидности вывода: ограничение, расширение, список и объединение. Здесь мы рассмотрим вывод с помощью ограничения и расширения, как наиболее популярные виды выведения.

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

Простые типы против сложных типов

Простые типы и сложные типы отличаются тем, что простые типы не могут иметь дочерние элементы или атрибуты, а сложные могут. Прослеживая иерархию типов далее по ветви простых типов, можно увидеть первый простой тип anySimpleType, также представляющий собой тип, который можно использовать в реальных описаниях. W3C XML Schemas содержат 44 встроенных простых типа, каждый из которых происходит от anySimpleType, и все они, кроме трех, выведены ограничением. Ни один из них не выведен расширением. Расширение простого типа означало бы добавление дочернего элемента или атрибута, а это противоречит определению простого типа в W3C Schemas и поэтому запрещено.

Выведение нового простого типа

С точки зрения иерархии типов вывести новый простой "myNameType" тип, ограничивающий свой базовый тип, "string" до определенного фиксированного множества - например, "Don Smith", - должно быть достаточно просто. Вот фрагмент W3C XML Schema для выражения myNameType:

<simpleType name="myNameType">
  <restriction base="string">
    <enumeration value="Don Smith" />
  </restriction>
</simpleType>

Как видно из примера, XML Schema для определения этого типа точно следует иерархии типов - за исключением элемента перечисления, одной из двенадцати фасет, используемых для определения типов. Рассмотрение фасет не входит в нашу задачу, поскольку мы занимаемся связями между иерархией типов и синтаксисом Schema для выражения типов. В данном случае это было использовано для полноты примера. (См. Table B1.a.Simple Types and Applicable Facets в Schema Part 0: Primer for a convenient list of facets.)

Теперь необходимо только связать myNameType с элементом, и затем можно будет использовать этот тип в XML-документе. Таким образом, объявление элемента будет таким:

<element name="employee" type="dc:myNameType" />
В примере XML-документа используем <dc:employee>Don Smith</dc:employee>.

А что же сложные типы?

А следует ли синтаксис сложных типов логике иерархии типов? Да. Но диаграмма иерархии типов нам тут не поможет, поскольку не содержит двух ключевых характеристик сложных типов. Однако с пониманием этих двух характеристик сложные типы утратят свою загадочную сложность и станут достаточно понятными.

Две формы сложных типов

Сложные типы делятся на две группы: с простым и сложным контентом. И это подводит нас к третьему моменту, необходимому для понимания сложных типов: несмотря на то, что обе формы сложных типов допускают атрибуты, дочерние элементы разрешены только для имеющих сложный контент; для сложных типов с простым контентом разрешен только символьный контент.

Другими словами, различие между сложными типами с простым и сложным контентом заключается в том, что первые не допускают дочерних элементов, а последние допускают. Вот в чем дело. Обе формы сложных типов представлены в так называемом Schema Type Decision Tree (PDF) под ветвью сложных типов.

Предположим, что необходимо добавить атрибут к myNameType. Добавление атрибута к простому типу всегда перемещает его в иерархии в группу сложных типов. Как только попадаем на ветвь сложных типов, следует задать второй вопрос: необходимо ли, чтобы для нового типа был разрешен элементный контент? Если нет, тогда этот новый тип должен быть сложным типом с простым контентом. Затем мы просто берем dc:myNameType в качестве базового типа и расширяем его за счет добавления атрибута:

<complexType name="myNewNameType">
  <simpleContent>
    <extension base="dc:myNameType">
       <attribute name="position" type="string" />
    </extension>
  </simpleContent>
</complexType>

Теперь, после объявления элемента "employee" как имеющего тип myNewNameType, можно включить <dc:employee position="trainer"> Don Smith</dc:employee> в пример XML-документа.

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

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

Добавление контентной модели попрежнему является выводом нового типа из некого базового типа. Если не брать существующий сложный тип в качестве базы для нового вывода, что еще можно использовать для этой цели? Мы используем anyType. Подавляющее большинство типов, допускающих элементный контент, представляют собой ограничения anyType. Например:

<complexType name="myNewNameType">
  <complexContent>
    <restriction base="anyType">
       <sequence>
          <element name="name" type="string" />
          <element name="location" type="string" />
        </sequence>
       <attribute name="position" type="string" />
    </restriction>
  </complexContent>
</complexType
<element name="employee" type="dc:myNewNameType" />

Тип, ассоциированный с "employee", теперь имеет элемент с именем "name", сопровождаемый элементом с именем "location". Далее, сотрудники (personnel) могут иметь атрибут с именем "position":

<dc:employee position="trainer">
<dc:name>Don Smith</dc:name>
<dc:location>Dallas, TX</dc:location>
</dc:employee>

Логика, лежащая в основе этого синтаксиса, проста. Требовался тип, допускающий дочерние элементы. Это подразумевает сложный тип со сложным контентом, и в то же время вывод нового типа из базового. В данном случае мы ограничивали anyType. Можно было так же просто расширить другой тип. Мы добавили нашу контентную модель и объявление атрибута. Все. И это было очень просто.

Неожиданные аббревиатуры (сокращения)

А можно ли все это выразить более сжато? Да, можно. Существует сокращенная форма для всех определений сложных типов, имеющих сложный контент и ограничивающих anyType. Можно просто опустить элементы <complexContent> и <restriction base="anyType">:

<complexType name="myNewNameType">
  <sequence>
    <element name="name" type="string" />
     <element name="location" type="string" />
  </sequence>
  <attribute name="position" type="string" />
</complexType>

Это определение типа эквивалентно предыдущему. И это подводит нас к четвертому свойству, необходимому для понимания сложных типов: синтаксис по умолчанию для сложных типов представляет собой сложный контент, ограничивающий anyType. Почему мы не начали сразу с сокращенного варианта синтаксиса? Потому, что сокращения скрывают логику, лежащую в основе синтаксиса по умолчанию. Если все что вы видите - это <complexType>, сопровождаемый контентной моделью, абсолютно непонятно, почему сложные типы иногда имеют дочерние элементы <complexContent> или <simpleContent>, или, часто, наоборот, не имеют таковых. Теперь, когда мы знаем логику, лежащую в основе этих двух форм сложного типа, мы не будем удивляться при виде сложного типа, не имеющего ни <complexContent>, ни <simpleContent>. Мы знаем, как это выглядит по умолчанию.

Эти коварные пустые элементы

Написание определений типов для пустых элементов, кажется, противоречит интуиции, однако, к счастью, логика синтаксиса сложных типов актуальна и в этом случае. Запомните, что пустой элемент - это элемент, не имеющий ни контента с данными, ни дочерних элементов. Он может иметь атрибут. Но для примера давайте возьмем пустой элемент, не имеющий атрибутов.

Первое, что приходит в голову - это связать пустой элемент с простым типом. Но это не будет работать, так как простые типы допускают только контент с данными. Поэтому здесь должен быть сложный тип. Затем следует задаться вопросом, допустим ли здесь дочерний элемент? Нет. Тогда нам требуется <complexType> с <simpleContent>, верно?

Неверно. Сложные типы с простым контентом также допускают только контент с данными, а нам нужен пустой элемент. Поэтому нам ничего не остается, кроме <complexType> с <complexContent>, гарантирующих, что в элементе не будет никакого контента с данными. Однако нам не нужны и дочерние элементы, а сложный тип со сложным контентом допускает дочерние элементы. Но дело в том, что он не требуетe их. Что же мы делаем? Просто опускаем контентную модель в определении типа:

<complexType name="processingHook">
  <complexContent>
    <restriction base="anyType">
    </restriction>
  </complexContent>
</complexType>
 
<element name="callMyApp" type="dc:processingHook" />

Наше определение типа, теперь ассоциированное с элементом "callMyApp", позволяет разметке <callMyApp/> появиться в нашем примере XML-документа. Теперь применим синтаксис сложных типов по умолчанию к этому определению типа. Определение, эквивалентное выше приведенному, таково:

<complexType name="processingHook">
</complexType>

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

Если все это теперь ясно, у вас нет основания слушать чьи-то заявления о том, что можно использовать сложные типы, не понимая их сущности. Теперь вы можете использовать их осмысленно.