Принципы проектирования XML-схем: нужны ли производные сложные типы

Несмотря на то, что в спецификации консорциума W3C XML Schema содержится ряд конструкций, напоминающих понятия объектно-ориентированного программирования - в том числе определение производных типов и полиморфизм, реальная практика показывает, что они скорее усложняют схемы, обладая трудно уловимой взаимосвязью, которая чревата возникновением серьезных проблем, и зачастую могут быть заменены другими конструкциями XML Schema. В этой статье рассмотрены определение производных сложных типов посредством наложения ограничений и расширений, показаны достоинства и недостатки обоих подходов, предложены альтернативные решения.

 

 

Зачем проверять XML-документы на допустимость

Рекомендация консорциума W3C "XML Schema" - это всего лишь одна из множества спецификаций языков XML-схем: DTD, RELAX NG и XML Data-Reduced. Для описания структуры XML-документа в XML-схеме определяются допустимые элементы, которые могут находится в документе, порядок их следования, а также ограничения, накладываемые на определенные характеристики этих элементов. Все более широкое распространение языка XML и языков XML-схем выявило два основных сценария использования XML-схем для проверки допустимости XML- документа.

  1. Описание и обеспечение соблюдения контракта между авторами и получателями XML-документов: обычно XML-схема используется получателями и авторами XML-документов в качестве средства для понимания структуры передаваемого или формируемого документа. Схемы - это довольно сжатый машиночитаемый способ описания состава допустимого XML-документа, допустимого согласно отдельному XML-словарю. Таким образом, схема может рассматриваться как контракт между автором и получателем XML-документа. Как правило получатель сверяет передаваемый XML-документ с этим контрактом, проверив его допустимость по схеме.
    Это описание контракта охватывает широкий спектр сценариев использования языка XML, начиная бизнес-субъектами, обменивающимися XML-документами, и заканчивая приложениями, в которых используются конфигурационные XML-файлы.
  2. Формирование основы для обработки и хранения типизированных данных, представленных в виде XML-документов: популярность языка XML как способа представления жестко структурированных, строго типизированных данных, таких как, например, содержимое реляционной базы или объектов различных языков программирования, потребовала возможности описывать типы данных элементов XML-документа. В результате, появились языки схем XML Data и XML Data-Reduced, а затем и XML-схема W3C. Эти языки схем используются для преобразования входного информационного набора XML (XML infoset) в аннотированный информационный набор типов (type annotated infoset (TAI)), в котором информационные единицы элемента и атрибута снабжены (аннотированы) именами типов.
    В рекомендации консорциума W3C "XML Schema" описывается создание аннотированного информационного набора типов, появляющегося в результате проверки допустимости документа по схеме. При проверке документа по XML-схеме W3C входной информационный набор XML преобразуется в постверификационный информационный набор (post schema validation infoset (PSVI)), в котором помимо всего прочего содержатся аннотации типов. Однако, практический опыт показывает, что для создания аннотированных информационных наборов типов не требуется проведения полной проверки допустимости документа; как правило, большинство приложений, в которых используются XML-схемы для формирования строго типизированных XML-документов, например, для преобразования XML<->объект, не выполняет полной проверку допустимости документов, поскольку ряд конструкций XML-схемы W3C не соответствует понятиям предметной области.

В этой статье рассматриваются достоинства и недостатки определения производных сложных типов с точки зрения их влияния на указанные случаи использования XML-схемы.

Рассмотрение определения производных сложных типов посредством ограничений

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

  • замена факультативного атрибута обязательным;
  • изменение количества вхождений элемента, то есть когда новое число вхождений становится подмножеством первоначального (например, с minOccurs="1" и maxOccurs="unbounded" на minOccurs="2" и maxOccurs="4");
  • изменение значения атрибута, указывающего, что модель содержания элемента является пустой, с true на false;
  • замена типа элемента или атрибута подтипом (например, с xs:integer в описании базового типа на xs:positiveInteger в описании производного);
  • задание в объявлении элемента или атрибута его фиксированного значения.

Определение производного типа посредством ограничений в основном удобно в сочетании с абстрактными элементами или типами. Так, можно создать абстрактный тип, который содержит все характеристики ряда связанных моделей содержания, а затем ограничить его для создания каждой из требуемых моделей содержания. Этот прием был рассмотрен в сообщении Роджера Костелло (Roger Costello), направленного в адрес XML-DEV, в котором он свел PublicationType к MagazineType.

В приведенной ниже схеме, которая была позаимствована из предыдущих статей, используется ограничение сложного типа для получения из типа, характеризующего подписчика списка рассылки XML-DEV, типа, описывающего автора. В результате, можно проверить допустимость любого элемента, соответствующего типу DareObasanjo, как экземпляра типа XML-Deviant.

 

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- базовый тип --> <xs:complexType name="XML-Deviant"> <xs:sequence> <xs:element name="numPosts" type="xs:integer" minOccurs="0" maxOccurs="1" /> <xs:element name="signature" type="xs:string" nillable="true" /> <xs:element name="email" type="xs:string" minOccurs="0" maxOccurs="1" /> </xs:sequence> <xs:attribute name="firstSubscribed" type="xs:date" use="optional" /> <xs:attribute name="mailReader" type="xs:string"/> </xs:complexType> <!-- производный тип --> <xs:complexType name="DareObasanjo"> <xs:complexContent> <xs:restriction base="XML-Deviant"> <xs:sequence> <xs:element name="numPosts" type="xs:integer" minOccurs="1" /> <xs:element name="signature" type="xs:string" nillable="false" /> <xs:element name="email" type="xs:string" maxOccurs="0" /> </xs:sequence> <xs:attribute name="firstSubscribed" type="xs:date" use="required" /> <xs:attribute name="mailReader" type="xs:string" fixed="Microsoft Outlook" /> </xs:restriction> </xs:complexContent> </xs:complexType> </xs:schema>

 

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

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

В предыдущей статье "Как избежать запутанности" (Avoiding Complexity) из серии "Принципы проектирования XML-схем" автор объяснил почему необходимо быть осторожным при использовании определения сложных типов посредством ограничения:

"Правила определения сложных типов посредством наложения ограничений описаны в Разделах 3.4.6 и 3.9.6 Рекомендации W3C "XML Schema". Большинство багов в реализациях тесно связаны с этой функциональностью, и довольно часто при обсуждении различных нюансов получения таких производных типов разработчики высказывают самое серьезное недовольство. Более того, этот способ определения производных типов не полностью соответствует понятиям ни предметно-ориентированного программирования, ни теории реляционных баз данных, которые являются основными потребителями и создателями XML-данных".

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

 

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="XML-Deviant"> <xs:sequence> <xs:element name="numPosts" type="xs:integer" minOccurs="0" maxOccurs="1" /> <xs:element name="signature" type="xs:string" nillable="true" /> <xs:element name="email" type="xs:string" minOccurs="0" maxOccurs="1" /> </xs:sequence> <xs:attribute name="firstSubscribed" type="xs:date" use="optional" /> <xs:attribute name="mailReader" type="xs:string"/> </xs:complexType> <xs:complexType name="DareObasanjo"> <xs:sequence> <xs:element name="numPosts" type="xs:integer" minOccurs="1" /> <xs:element name="signature" type="xs:string" nillable="false" /> <xs:element name="email" type="xs:string" maxOccurs="0" /> </xs:sequence> <xs:attribute name="firstSubscribed" type="xs:date" use="required" /> <xs:attribute name="mailReader" type="xs:string" fixed="Microsoft Outlook" /> </xs:complexType> </xs:schema>

 

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

Для сценариев использования, в которых схема применяется для создания строго типизированного XML, получение производных типов посредством ограничения чревато возникновением проблем. В реляционной модели и традиционных положениях об определении производных типов в объектно-ориентированных языках программирования отсутствует возможность накладывать ограничения на факультативные элементы и атрибуты. Приведенный выше пример, в котором элемент email является факультативным в базовом типе, а в производном не может появляться, несовместим с нотацией получения производного типа с позиции объектно-ориентированного подхода; его также трудно смоделировать, используя таблицы реляционной базы данных. Аналогично, изменение признака, указывающего, что модель содержания типа является пустой, не является характеристикой, которая соответствует реляционной или объектно-ориентированной моделям. С другой стороны, пример, в котором не используется получение производного типа посредством ограничения, более просто моделировать в виде классов на языке объектно-ориентированного программирования или в виде реляционных таблиц. Это важно, если учесть, что это уменьшает рассогласование, возникающее при попытке отобразить содержание XML-документа в реляционную базу данных или преобразовать его в экземпляр объектно-ориентированного класса.

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

Рассмотрение определения производных сложных типов посредством расширения

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

В приведенной ниже схеме используется расширение сложного типа для получения из сложного типа, характеризующего подписчика списка рассылки XML-DEV, типа, описывающего автора. Таким образом, экземпляр типа DareObasanjo необязательно является допустимым экземпляром типа XML-Deviant.

 

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- базовый тип --> <xs:complexType name="XML-Deviant"> <xs:sequence> <xs:element name="numPosts" type="xs:integer" minOccurs="0" maxOccurs="1" /> <xs:element name="email" type="xs:string" /> </xs:sequence> <xs:attribute name="firstSubscribed" type="xs:date" use="optional" /> <xs:attribute name="lastPostDate" type="xs:date" use="optional" /> </xs:complexType> <!-- производный тип --> <xs:complexType name="DareObasanjo"> <xs:complexContent> <xs:extension base="XML-Deviant"> <xs:sequence> <xs:element name="signature" type="xs:string" /> </xs:sequence> <xs:attribute name="mailReader" type="xs:string" fixed="Microsoft Outlook" /> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema>

 

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

Кажется, что для пользователей, которые хотят использовать XML-схему для проверки XML-документа на соответствие контракту, определение производных сложных типов посредством расширений является отличным способом разложить на компоненты аспекты схемы и повторно воспользоваться ими. Однако, первое впечатление обманчиво - взаимодействие с другими конструкциями XML-схемы W3C, как, например, группами подстановки (substitution groups) и xsi:type, превращает использование определения производных сложных типов посредством расширений в разряд трудно контролируемых задач. Рассмотрим, например, следующее объявление элемента:

 

<xs:element name="xml-deviant" type="XML-Deviant" />

 


 

в котором объявляется элемент xml-deviant, тип которого, XML-Deviant, является сложным типом, описанным в приведенной выше схеме. Оба XML-элемента, приведенные в следующем фрагменте, являются допустимыми в соответствии с этим объявлением элемента xml-deviant:
 

 

<xml-deviant firstSubscribed="1999-05-31" > <email>johndoe@example.com</email> </xml-deviant> <xml-deviant xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="DareObasanjo" firstSubscribed="1999-05-31" mailReader="Microsoft Outlook"> <email>dareo@online.microsoft.com</email> <signature>XML is about data not objects, that is the zen of XML.</signature> </xml-deviant>

 

Несмотря на то, что в объявлении этого элемента явно указано, что типом элемента xml-deviant является сложный тип XML-Deviant, экземпляр может замещать это объявление в схеме, используя атрибут xsi:type, при условии, что этот новый тип является подтипом первоначального типа. Это означает, что по умолчанию даже если элемент успешно прошел проверку на допустимость, он необязательно соответствует модели содержания, по которой, как полагает получатель, он проверяется. Схожая проблема возникает, когда рассматриваемое объявление элемента назначается заголовком (head) групп подстановок.

Существует два способа обойти эту потенциальную проблему, возникающую при определении производных сложных типов посредством расширений. Первый заключается в блокировании подстановки или определении производного типа посредством размещения атрибута block или final в объявлении элемента или в описании сложного типа. Аналогично, атрибут blockDefault или finalDefault может быть добавлен в элемент xs:schema для указания, какой вид подстановок или определения производных типов неразрешен в этой схеме. Второй способ состоит в использовании поименованных групп моделей (xs:group) и групп атрибутов для разбиения схемы на модули - как альтернатива определению производных сложных типов посредством расширений. Ниже приведена схема, которая была рассмотрена в предыдущем раздела и в которую были добавлены поименованные группы моделей.

 

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="XML-Deviant"> <xs:group ref="XMLDeviantGrp" /> <xs:attributeGroup ref="XMLDeviantAttrGrp" /> </xs:complexType> <xs:complexType name="DareObasanjo"> <xs:sequence> <xs:group ref="XMLDeviantGrp" /> <xs:element name="signature" type="xs:string" /> </xs:sequence> <xs:attributeGroup ref="XMLDeviantAttrGrp" /> <xs:attribute name="mailReader" type="xs:string" fixed="Microsoft Outlook" /> </xs:complexType> <xs:group name="XMLDeviantGrp"> <xs:sequence> <xs:element name="numPosts" type="xs:integer" minOccurs="0" maxOccurs="1" /> <xs:element name="email" type="xs:string" minOccurs="0" maxOccurs="1" /> </xs:sequence> </xs:group> <xs:attributeGroup name="XMLDeviantAttrGrp"> <xs:attribute name="firstSubscribed" type="xs:date" use="optional" /> <xs:attribute name="lastPostDate" type="xs:date" use="optional" /> </xs:attributeGroup> </xs:schema>

 

Для сценариев использования, которые применяют строго типизированные XML-документы, определение производных сложных типов посредством расширений представляет хотя и отличный, но родственный ряд проблем. В случае, если XML-схема используется в качестве основы преобразования между XML и объектно-ориентированной или реляционной моделями, такое определение производных сложных типов не оказывается проблематичным. Однако, при обработки таких строго типизированных XML-документов с помощью языков программирования, поддерживающих схему, как, например, XQuery или XSLT 2.0, возникают определенные сложности. XQuery - это статически типизированный язык, а это значит, что ожидается, что он обнаружит ошибки, связанные с типом, во время компиляции, а не во воремя исполнения. Следующий запрос, заданный к приведенному выше примеру, чреват возникновением проблем:

 

for $x in //xml-deviant return $x/signature

 

С одной стороны, это выражение должно привести к статической ошибке, поскольку элемент xml-deviant объявлен как элемент типа XML-Deviant, который не содержит элемент signature. С другой стороны, поскольку у XML-Deviant существует подтип, у которого в модели содержания есть элемент signature и который, следовательно, мог бы быть адресатом директивы xsi:type, это ошибка не должна расцениваться как статическая. Обе позиции являются допустимыми, но независимо от того, что выберет XQuery, всегда найдутся люди, которые будут ожидать противоположенное. Разработчики, знакомые с XPath, могут решить, что этот запрос будет работать, в то время как, те, кто освоился со статически типизированными языками, посчитает его эквивалентом следующего выражения и, таким образом, ошибкой:

 

foreach(xmldeviant b in list) { yield b.signature; // static type error. }

 

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

Заключение

С учетом нынешнего уровня развития технологий возможность определения производных сложных типов в соответствии с положениями XML-схемы W3C скорее добавит сложности, а не упростит ситуацию в двух наиболее общих случаях использования схемы. Для сценариев проверки допустимости документов получение производных типов посредством ограничений имеет минимальную ценность, но получение производных типов посредством расширений - это удобный способ применить модульный подход и воспользоваться принципом повторного использования. Однако, следует внимательно изучить последствия использования подстановок различных типов (xsi:type и группы подстановок) при определении производных типов посредством расширения в сценариях, задействованных в проверке допустимости документа.

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




Рекомендуем почитать

 

Добавить комментарий


Ваше имя:


Комментарий:


Введите: Картинка