Пример XML схемы

В этой главе будет показано, как писать XML схемы. Также вы узнаете, что схемы можно писать разными способами.

XML документ

Давайте посмотрим на следующий XML документ под названием "shiporder.xml":


<?xml version="1.0" encoding="UTF-8"?>
<shiporder orderid="889923"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="shiporder.xsd">
   <orderperson>John Smith</orderperson>
   <shipto>
      <name>Ola Nordmann</name>
      <address>Langgt 23</address>
      <city>4000 Stavanger</city>
      <country>Norway</country>
   </shipto>
   <item>
      <title>Empire Burlesque</title>
      <note>Special Edition</note>
      <quantity>1</quantity>
      <price>10.90</price>
   </item>
   <item>
      <title>Hide your heart</title>
      <quantity>1</quantity>
      <price>9.90</price>
   </item>
</shiporder>

Приведенный выше XML документ состоит из корневого элемента shiporder с обязательным атрибутом orderid. Элемент shiporder содержит три дочерних элемента: orderperson, shipto и item. Элемент item используется дважды и содержит элемент title, необязательный элемент note, а также элементы quantity и price.

Строка xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" говорит XML парсеру, что этот документ должен быть проверен на соответствие схеме. Строка xsi:noNamespaceSchemaLocation="shiporder.xsd" указывает, где именно находится схема (в данном случае она находится в той же папке, что и файл "shiporder.xml").

Создание XML схемы

Теперь для приведенного выше XML документа создадим XML схему.

Создадим новый файл, который назовем "shiporder.xsd". Для создания XML схемы будем просто следовать за структурой XML документа и определять каждый встреченный элемент. Начнем со стандартной XML декларации, за которой опишем элемент xs:schema, который и определяет саму схему:


<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   ...
</xs:schema>

Здесь мы используем стандартное пространство имен (xs) и URI, ассоциированный с этим пространством имен, который имеет стандартное значение http://www.w3.org/2001/XMLSchema.

Теперь мы должны определить элемент shiporder. У этого элемента есть атрибут, и он содержит другие элементы, поэтому мы рассматриваем его как элемент составного типа. Определения дочерних элементов элемента shiporder поместим в декларацию xs:sequence, что задает жесткую последовательность подэлементов:


<xs:element name="shiporder">
   <xs:complexType>
      <xs:sequence>
         ...
      </xs:sequence>
   </xs:complexType>
</xs:element>

Теперь определим элемент orderperson, который будет простого типа (так как он не содержит ни атрибуты, ни другие элементы). Его тип (xs:string) имеет префикс пространства имен, ассоциированного с XML схемой, что указывает на использование предопределенного типа данных:


<xs:element name="orderperson" type="xs:string"/>

Теперь нам нужно определить два элемента составного типа: shipto и item. Начнем с определения элемента shipto:


<xs:element name="shipto">
   <xs:complexType>
      <xs:sequence>
         <xs:element name="name" type="xs:string"/>
         <xs:element name="address" type="xs:string"/>
         <xs:element name="city" type="xs:string"/>
         <xs:element name="country" type="xs:string"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>

При помощи схем мы можем определить число возможных вхождений любого элемента. В этом нам помогут атрибуты maxOccurs и minOccurs. Атрибут maxOccurs задает максимальное число вхождений элемента, а атрибут minOccurs задает минимальное число вхождений. По умолчанию значение обоих атрибутов равно 1.

Теперь определим элемент item. Этот элемент может использоваться неограниченное число раз внутри элемента shiporder. Определить такую особенность элемента item позволяет присваивание атрибуту maxOccurs значения "unbounded". Это означает, что элемент item может использоваться столько раз, сколько нужно автору документа. Обратите внимание, что элемент note опционален. Определим это установив атрибут minOccurs в нулевое значение:


<xs:element name="item" maxOccurs="unbounded">
   <xs:complexType>
      <xs:sequence>
         <xs:element name="title" type="xs:string"/>
         <xs:element name="note" type="xs:string" minOccurs="0"/>
         <xs:element name="quantity" type="xs:positiveInteger"/>
         <xs:element name="price" type="xs:decimal"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>

Теперь мы можем декларировать атрибут элемента shiporder. Поскольку это обязательный атрибут, используем определение use="required".

Примечание: Атрибуты должны всегда декларироваться последними:


<xs:attribute name="orderid" type="xs:string" use="required"/>

Вот полный код файла схемы "shiporder.xsd":


<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="shiporder">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="orderperson" type="xs:string"/>
            <xs:element name="shipto">
               <xs:complexType>
                  <xs:sequence>
                     <xs:element name="name" type="xs:string"/>
                     <xs:element name="address" type="xs:string"/>
                     <xs:element name="city" type="xs:string"/>
                     <xs:element name="country" type="xs:string"/>
                  </xs:sequence>
               </xs:complexType>
            </xs:element>
            <xs:element name="item" maxOccurs="unbounded">
               <xs:complexType>
                  <xs:sequence>
                     <xs:element name="title" type="xs:string"/>
                     <xs:element name="note" type="xs:string" minOccurs="0"/>
                     <xs:element name="quantity" type="xs:positiveInteger"/>
                     <xs:element name="price" type="xs:decimal"/>
                  </xs:sequence>
               </xs:complexType>
            </xs:element>
         </xs:sequence>
         <xs:attribute name="orderid" type="xs:string" use="required"/>
      </xs:complexType>
   </xs:element>
</xs:schema>

Разделение схемы

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

Следующий способ компоновки схемы заключается в том, что сначала определяются все элементы и атрибуты, а затем на эти определения создаются ссылки при помощи атрибута ref.

Ниже приводится новая компоновка файла схемы ("shiporder.xsd"):


<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <!-- определение простых элементов -->
   <xs:element name="orderperson" type="xs:string"/>
   <xs:element name="name" type="xs:string"/>
   <xs:element name="address" type="xs:string"/>
   <xs:element name="city" type="xs:string"/>
   <xs:element name="country" type="xs:string"/>
   <xs:element name="title" type="xs:string"/>
   <xs:element name="note" type="xs:string"/>
   <xs:element name="quantity" type="xs:positiveInteger"/>
   <xs:element name="price" type="xs:decimal"/>

   <!-- определение атрибутов -->
   <xs:attribute name="orderid" type="xs:string"/>

   <!-- определение составных элементов -->
   <xs:element name="shipto">
      <xs:complexType>
         <xs:sequence>
            <xs:element ref="name"/>
            <xs:element ref="address"/>
            <xs:element ref="city"/>
            <xs:element ref="country"/>
         </xs:sequence>
      </xs:complexType>
   </xs:element>

   <xs:element name="item">
      <xs:complexType>
         <xs:sequence>
            <xs:element ref="title"/>
            <xs:element ref="note" minOccurs="0"/>
            <xs:element ref="quantity"/>
            <xs:element ref="price"/>
         </xs:sequence>
      </xs:complexType>
   </xs:element>

   <xs:element name="shiporder">
      <xs:complexType>
         <xs:sequence>
            <xs:element ref="orderperson"/>
            <xs:element ref="shipto"/>
            <xs:element ref="item" maxOccurs="unbounded"/>
         </xs:sequence>
         <xs:attribute ref="orderid" use="required"/>
      </xs:complexType>
   </xs:element>
</xs:schema>

Использование поименованых типов

Третий способ компоновки схемы предполагает определение классов или типов, которые позволяют повторное использование определений элементов. Это становится возможным, если дать имена элементам simpleTypes и complexTypes, а затем указать на них при помощи атрибута type.

Третий способ компоновки файла схемы ("shiporder.xsd"):


<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:simpleType name="stringtype">
      <xs:restriction base="xs:string"/>
   </xs:simpleType>

   <xs:simpleType name="inttype">
      <xs:restriction base="xs:positiveInteger"/>
   </xs:simpleType>

   <xs:simpleType name="dectype">
      <xs:restriction base="xs:decimal"/>
   </xs:simpleType>

   <xs:simpleType name="orderidtype">
      <xs:restriction base="xs:string">
         <xs:pattern value="[0-9]{6}"/>
      </xs:restriction>
   </xs:simpleType>

   <xs:complexType name="shiptotype">
      <xs:sequence>
         <xs:element name="name" type="stringtype"/>
         <xs:element name="address" type="stringtype"/>
         <xs:element name="city" type="stringtype"/>
         <xs:element name="country" type="stringtype"/>
      </xs:sequence>
   </xs:complexType>

   <xs:complexType name="itemtype">
      <xs:sequence>
         <xs:element name="title" type="stringtype"/>
         <xs:element name="note" type="stringtype" minOccurs="0"/>
         <xs:element name="quantity" type="inttype"/>
         <xs:element name="price" type="dectype"/>
      </xs:sequence>
   </xs:complexType>

   <xs:complexType name="shipordertype">
      <xs:sequence>
         <xs:element name="orderperson" type="stringtype"/>
         <xs:element name="shipto" type="shiptotype"/>
         <xs:element name="item" maxOccurs="unbounded" type="itemtype"/>
      </xs:sequence>
      <xs:attribute name="orderid" type="orderidtype" use="required"/>
   </xs:complexType>

   <xs:element name="shiporder" type="shipordertype"/>
</xs:schema>

Элемент restriction указывает на то, что тип данных является производным от типов данных из пространства имен W3C XML Schema. Таким образом, следующий фрагмент кода означает, что значение элемента или атрибута должно быть строковым:


<xs:restriction base="xs:string">

Однако гораздо чаще элемент restriction используется для накладывания ограничений на элементы. Посмотрите на следующие строки из приведенной выше схемы:


<xs:simpleType name="orderidtype">
   <xs:restriction base="xs:string">
      <xs:pattern value="[0-9]{6}"/>
   </xs:restriction>
</xs:simpleType>

Этот фрагмент кода указывает, что значение элемента или атрибута должно быть строковым, ровно шесть символов в длину, и этими символами должны быть цифры от 0 до 9.