XML - статьи


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


В предыдущей статье "Как избежать запутанности" (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>


Кажется, что для пользователей, которые хотят использовать 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="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.



<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.


Содержание раздела