As we have seen in the previous section, a DTD defines constraints on the order and
nesting of elements in an XML file, but the type of constraints is limited and does not
allow validation of the character content of elements, except for enumeration values and
ID/IDREF
. There are also other drawbacks: all element names in a DTD must
be unique and thus combining separately developed DTDs can become quite cumbersome.
Moreover, the DTD file is not a well-formed XML file, thus one cannot easily use an
XML tool to create or process it. This is why XML Schema has been introduced with a
comprehensive set of elementary types and a way to combine them to create new types. The
concept of namespaces (presented in
Section 2.1) is also used in order to facilitate the combination of
independent files without name clashes.
A Schema is a well-formed XML file (usually with a
.xsd
extension) that defines types used to validate the elements of an XML file. In a way
similar to variable declarations in a programming language, we can define types. We follow
the Java convention of starting type identifiers with an upper case letter. Element
identifiers start with a lower case letter. In a name comprising more than one word, each
word starts with an uppercase letter. No underscore or dash are used. For most elements, we
define a type having the same name as the element, but capitalized, instead of using inline
definitions of embedded elements. In a Schema, there are two kinds of types: simple and
complex.
Simple types define constraints on the text content of an element which cannot
contain any element.
A complex type can contain nested elements.
There are many different ways of organizing a Schema as described by Van der Vlist [60]. One can either use a Russian doll approach in which a single element is defined with its nested elements defined internally. Another approach is to use a bottom-up approach in which the elements are defined before they are used in more complex elements. It is also possible to use a top-down approach that first defines the higher level elements before defining the lower level elements. All these styles of definitions are possible and we will sometimes use a mix of them in order to show some features of XML Schema.
Table 3.2
presents the XML elements we use in our example to define the types needed for the
validation of our wine catalog and cellar book. Since a schema is itself an XML file, it
is important to distinguish the elements defining the Schema from the elements being
defined. This is ensured by using a different namespace for the defining element (definiens)
such as
xs:
as prefix (xsd:
is also commonly used). A defined element (definiendum) does not have a
prefix, it is in the default namespace. Contrarily to a DTD, an XML Schema is a valid
XML file and it can be validated using the XML Schema of XML Schemas, usually provided with
in all XML editors.
Table 3.2. XML Schema syntax reminder
<xs:schema targetNameSpace="URI"> xs:import* {xs:simpleType | xs:complexType | xs:element | xs:group}* </xs:schema> |
<xs:import nameSpace="URI" schemaLocation="URI"/> |
<xs:simpleType name="NCName"> xs:restriction </xs:simpleType> |
<xs:complexType name="NCName" mixed="true"> {xs:choice | xs:sequence | xs:group}? xs:attribute* </xs:complexType> |
<xs:element name="QName" type="TName"/> |
<xs:element name="QName" ref="EName"/> |
<xs:element name="QName"> {xs:simpleType | xs:complexType}? {xs:unique | xs:key | xs:keyref}* </xs:element> |
<xs:sequence {min|max}occurs="nonNegativeInteger|unbounded"> {xs:element | xs:choice | xs:sequence | xs:group}* </xs:sequence> |
<xs:choice {min|max}occurs="nonNegativeInteger|unbounded"> {xs:element | xs:choice | xs:sequence | xs:group}* </xs:choice> |
<xs:group name="NCName"> {xs:choice | xs:sequence}* </xs:group> |
<xs:attribute name="NCName" type="TName" use="required"/> |
<xs:restriction base="TName"> <xs:{max|min}{in|ex}clusive value="anySimpleType"/> | <xs:{max|min}length value ="nonNegativeInteger" | <pattern value = "regExp" | <enumeration value = "anyValue" </xs:restriction> |
<xs:{unique|key} name="NCName"> xs:selector xs:field+ </xs:{unique|key}> |
<xs:keyref name="NCName" refer="NCName"> xs:selector xs:field+ </xs:keyref> |
<xs:{selector|field} xpath="XPathExpr"/> |
A reminder of the subset of the XML Schema syntax used in Example 3.3 and Example 3.4. Names in italics
refer to other elements. NCName
(non-colonized name) is a name without a
namespace prefix. Regular expressions syntax is given in Table B.1.
An XML Schema has an
xs:schema
element as root which can contain different kinds of definition elements.
xs:import
allows the combination of different schemas into a single one; in our case, we
have a schema for the wine catalog which is imported into that of the cellar
book.
xs:simpleType
defines additional constraints on predefined types; this is explained further in
Section 3.2.1.
xs:complexType
defines a new type in terms of a choice or a sequence between other types.
xs:group
gives a name to an incomplete type that can be used as building block for other
types.
xs:element
is the fundamental way of defining an element that can appear in an instance
file. It can be given either with a name and a type. It can refer to another
element definition or it can be defined with an anonymous simple or complex type
followed by keys and keyrefs definitions.
xs:sequence
combines other elements by making sure that they occur sequentially.
xs:choice
combines other elements by making sure that only one of them occurs.
xs:all
combines other elements by making sure that they all occur but in any order.
xs:attribute
gives the name and the simple type associated with an attribute. Attributes are
not ordered and optional unless their are given the value
required
to their
use
attributes. Note that
xs:attribute
s are given at the end of the type definition, even
though they appear in the start-tag.
xs:restriction
gives range, pattern constraints on the value of a simple type.
enumeration
is to limit the allowed value to one of a given list.
xs:key
,
xs:unique
and
xs:keyref
define cooccurrence constraints that generalize the notion of ID/IDREF. They
will not be explained in this document.
In the rest of this section, we first give in Example 3.3 and Example 3.4 the XML Schemas that correspond respectively to the DTDs of Example 3.1 and Example 3.2. Figure 3.1 gives the overall structure of the XML Schema of Example 3.3. Figure 3.2 gives the overall structure of the Schema in Example 3.4. As we will see, the validation of the text content of the elements can be much more thorough with an XML Schema than with a DTD.
We will then explain the structure of the type system: first simple types (Section 3.2.1) and then complex types (Section 3.2.2).
Figure 3.1. Graphical view of the schema for the cellar book
A name in a rectangular box is an
element
name or, if preceded by
@
, an
attribute
name. A
complex type
name is preceded by a square and a
simple type
by a triangle. A
sequence
is shown with 4 dots horizontally aligned in an hexagon and a
choice
with 4 dots aligned vertically (see
Figure 5.2
for an example). A
+
after a box indicates that further details have been omitted. Three small squares in
front of an element name either indicates that its definition will be referred to
somewhere else in the schema; the reference is indicated by a small arrow at the
bottom right of the rectangle. This figure was produced by the <oXygen/> XML
editor from the XML Schema file given in
Example 3.3.
Example 3.3. [CellarBook.xsd
] XML Schema for the cellar book, validation of the
instance file in
Example 2.2.
This file can be compared with the DTD shown in Example 3.1.
1 <?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:cat="http://www.iro.umontreal.ca/lapalme/wine-catalog"> 5 <xs:import namespace="http://www.iro.umontreal.ca/lapalme/wine-catalog" schemaLocation="WineCatalog.xsd"/> <xs:element name="cellar">
<xs:complexType> 10 <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="wine" type="Wine"/>
</xs:sequence> </xs:complexType> </xs:element> 15 <xs:complexType name="Wine">
<xs:sequence> <xs:element name="purchaseDate" type="xs:date"/>
<xs:element name="quantity" type="xs:nonNegativeInteger"/> 20 <xs:element name="rating" minOccurs="0"> <xs:complexType> <xs:attribute name="stars" type="xs:positiveInteger"/>
</xs:complexType> </xs:element> 25 <xs:element name="comment" type="cat:Comment" minOccurs="0"/> </xs:sequence> <xs:attribute name="code" type="xs:IDREF" use="required"/>
</xs:complexType> 30 <xs:element name="name">
<xs:complexType> <xs:all>
<xs:element name="first" type="xs:string" minOccurs="
0"/> 35 <xs:element name="family" type="xs:string" minOccurs="0"/> <xs:element name="initial" type="xs:string" minOccurs="0"/> </xs:all> </xs:complexType> </xs:element> 40 <xs:element name="cellar-book">
<xs:complexType> <xs:sequence> 45 <xs:element ref="cat:wine-catalog"/> <xs:element name="owner" type="Owner"/> <xs:element name="location" minOccurs="0"> <xs:complexType> <xs:group ref="address"/> 50 </xs:complexType> </xs:element> <xs:element ref="cellar"/> </xs:sequence> </xs:complexType> 55 </xs:element> <xs:group name="address">
<xs:sequence> <xs:element name="street" type="xs:string"/> 60 <xs:element name="city" type="xs:string"/> <xs:element name="province" type="ProvinceCA"/> <xs:element name="postal-code" type="PostalCodeCA"/>
</xs:sequence> </xs:group> 65 <xs:simpleType name="ProvinceCA"> <!-- http://www.canadapost.ca/tools/pg/manual/b03-e.asp#c012 --> <xs:restriction base="xs:string"> <xs:enumeration value="AB"/> 70 <xs:enumeration value="BC"/> <xs:enumeration value="MB"/> <xs:enumeration value="NB"/>
<xs:enumeration value="NL"/> <xs:enumeration value="NT"/> 75 <xs:enumeration value="NS"/> <xs:enumeration value="NU"/> <xs:enumeration value="ON"/> <xs:enumeration value="QC"/> <xs:enumeration value="SK"/> 80 <xs:enumeration value="YT"/> </xs:restriction> </xs:simpleType> <xs:complexType name="Owner">
85 <xs:sequence> <xs:element ref="name"/> <xs:group ref="address"/> </xs:sequence> </xs:complexType> 90 <xs:simpleType name="PostalCodeCA">
<xs:restriction base="xs:string"> <xs:pattern value="[A-Z][0-9][A-Z] [0-9][A-Z][0-9]"/> </xs:restriction> 95 </xs:simpleType> </xs:schema>
|
Gets the types defined in the schema for the wine catalog, which uses a
different namespace, the one identified by the
|
|
Element
|
|
Definition of element
|
|
The type
|
|
The purchase date is of the predefined XML type
|
|
The rating is an empty element with an attribute
|
|
This attribute is the link between the wines defined by the quantities in
the cellar and their description in the catalog. Being of type |
|
The element
|
|
Each component of a name is a string. |
|
As all elements are optional ( |
|
A cellar book is a list of elements starting with the wine catalog,
followed by the
|
|
An address is sequence of four elements indicating the street address,
city, the province in Canada and a Canadian postal code. An address is not
an element but a group that can be used to define other elements. Here it is
used within the
|
|
A postal code is a string matching a regular expression defined in the
|
|
The province is indicated by a two-letter code chosen among a predefined list. |
|
An owner is a
|
|
A Canadian postal code is composed of two groups of three characters alternating between a capital letter and a number. The two groups are separated by a space. |
Figure 3.2. Graphical view of the schema for the wine catalog
Graphical view of the schema for the wine catalog (Example 3.4). See the caption of Figure 3.1 for an explanation of the symbols used in the figure.
Example 3.4. [WineCatalog.xsd
] XML Schema file to validate the instance
document shown in
Example 2.3.
This file can be compared with the DTD shown in Example 3.2.
1 <?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'elementFormDefault="qualified"
attributeFormDefault="unqualified"
5 xmlns:cat ="http://www.iro.umontreal.ca/lapalme/wine-catalog" targetNamespace="http://www.iro.umontreal.ca/lapalme/wine-catalog">
<!-- needed because this schema will be imported --> <xs:import namespace="http://www.w3.org/XML/1998/namespace" 10 schemaLocation="http://www.w3.org/2001/xml.xsd"/> <xs:element name="wine-catalog">
<xs:complexType> <xs:sequence minOccurs="0" maxOccurs="unbounded"> 15 <xs:element name="wine" type="cat:Wine"/> </xs:sequence> <!-- needed because this schema will be imported...--> <xs:attribute ref="xml:base"/> </xs:complexType> 20 </xs:element> <xs:complexType name="Wine">
<xs:sequence> <xs:element name="properties" type="cat:Properties"/> 25 <xs:element name="origin" type="cat:Origin"/> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="tasting-note" type="cat:Comment" minOccurs="0"/> <xs:element name="food-pairing" 30 type="cat:Comment" minOccurs="0"/> <xs:element name="comment" type="cat:Comment" minOccurs="0"/> </xs:choice> <xs:element name="price" type="xs:decimal" ></xs:element> 35 <xs:element name="year" type="xs:gYear"/> </xs:sequence> <xs:attribute name="name" type="xs:string" use="required"/> <xs:attribute name="appellation" type="xs:string"/> <xs:attribute name="classification" type="xs:string"/> 40 <xs:attribute name="code" type="xs:ID"/>
<xs:attribute name="format" type="cat:Format"/> </xs:complexType> <xs:complexType name="Properties">
45 <xs:sequence> <xs:element name="color" type="cat:Color"/> <xs:element name="alcoholic-strength" type="cat:Percentage"/> <xs:element name="nature" type="xs:string" minOccurs="0"/> </xs:sequence> 50 </xs:complexType> <xs:complexType name="Origin">
<xs:sequence> <xs:element name="country" type="xs:string"/> 55 <xs:element name="region" type="xs:string"/> <xs:element name="producer" type="xs:string"/> </xs:sequence> </xs:complexType> 60 <xs:simpleType name="Format">
<xs:restriction base="xs:string"> <xs:enumeration value="375ml"/> <xs:enumeration value="750ml"/> <xs:enumeration value="1l"/> 65 <xs:enumeration value="magnum"> <xs:annotation> <xs:documentation> 1.5 litres</xs:documentation> </xs:annotation> </xs:enumeration> 70 <xs:enumeration value="jeroboam"> <xs:annotation> <xs:documentation> 3 litres</xs:documentation> </xs:annotation> </xs:enumeration> 75 <xs:enumeration value="rehoboam"> <xs:annotation> <xs:documentation> 4.5 litres</xs:documentation> </xs:annotation> </xs:enumeration> 80 <xs:enumeration value="mathusalem"> <xs:annotation> <xs:documentation> 6 litres</xs:documentation> </xs:annotation> </xs:enumeration> 85 <xs:enumeration value="salmanazar"> <xs:annotation> <xs:documentation> 9 litres</xs:documentation> </xs:annotation> </xs:enumeration> 90 <xs:enumeration value="balthazar"> <xs:annotation> <xs:documentation>12 litres</xs:documentation> </xs:annotation> </xs:enumeration> 95 <xs:enumeration value="nabuchodonosor"> <xs:annotation> <xs:documentation>15 litres</xs:documentation> </xs:annotation> </xs:enumeration> 100 </xs:restriction> </xs:simpleType> <xs:complexType name="Comment" mixed="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded"> 105 <xs:choice> <xs:element name="emph" type="xs:string"/> <xs:element name="bold" type="xs:string"/> </xs:choice> </xs:sequence> 110 </xs:complexType> <xs:simpleType name="Color">
<xs:restriction base="xs:string"> <xs:enumeration value="red"/> 115 <xs:enumeration value="white"/> <xs:enumeration value="rosé"/> </xs:restriction> </xs:simpleType> 120 <xs:simpleType name="Percentage">
<xs:restriction base="xs:decimal"> <xs:minInclusive value="0"/> <xs:maxInclusive value="100"/> <xs:fractionDigits value="2"/> 125 </xs:restriction> </xs:simpleType> </xs:schema>
|
Start of the schema for the wine catalog giving
|
|
If a schema has a target namespace such as in
line 6‑ |
|
|
|
Defines the namespace that will be used for the elements defined by this schema. |
|
The wine catalog is a (possibly empty) sequence of wine definitions. In
order that references to internal elements remain valid when the instance
file validated by this schema is imported by another file, an
|
|
A wine entry of the catalog is a list of properties: its origins followed by optional tasting notes, food pairing suggestions and comments, its price and its production year. Other information such as the name, the appellation, the classification, the code and the format are given as attribute strings. |
|
A wine in the catalog is assigned a unique code of type |
|
|
|
|
|
|
|
The
|
|
|
|
A percentage is a number between 0 and 100 that must include 2 digits after the decimal point. |
Figure 3.3. Built-in datatypes for XML Schema
Built-in datatypes for XML Schema.
ur-type
s serve as root of the type hierarchy for all derivations.
ur
is the German prefix meaning
ancestral
such as in
Ursprung
(beginning). Figure taken from section 3 of XML Schema Part 2: Datatypes
[9].
A simple type is a primitive datatype, roughly corresponding to
PCDATA
in DTD, such as
xs:string
,
xs:decimal
,
xs:double
,
xs:date
(XML has 19 of them, shown in
Figure 3.3) or a derivation of a primitive datatype. A derivation is
a restriction on the original type such as constraining the maximum length of a string,
giving a list of acceptable values, or requiring that the value matches a regular
expression.
Figure 3.3
shows a number of built-in derived types and types that are derived from them (i.e.
appear under them). We can see uses of simple types in
Example 3.3:
stars
(line 22‑) which must not only be an integer but a positive
one,
first
(line 34‑) which is a
string
(essentially the same thing as a
#PCDATA
in a DTD).
Users can also define their own simple types, which cannot have any internal element,
using the
xs:simpleType
element. A simple type definition can constrain a string to be one of many choices (ProvinceCA
on
line 72‑) or to match a regular expression (
PostalCodeCA
on
line 91‑).
A new simple type can also be created using a
list
(allowing a series of primitive type values) or a
union
(allowing one of many primitive types).
It is thus possible to define a whole gamut of types. These are quite straightforward,
although the
the devil is in the details
described in
[45]
and
[9].
Unlike a simple type, a complex type can contain element declarations, element references and attributes declarations. We will illustrate some of these possibilities with Example 3.3.
An element declaration is done with an
xs:element
specifying the name of the element and its type, which can either be defined as the
value of the element, such as
cellar
(line 8‑), or by indicating the type with the
type
attribute such as in
wine
(line 11‑) or
purchaseDate
(line 18‑).
A complex type is defined either by a sequence of elements contained in an
xs:sequence
element (see
cellar
on
line 8‑) or by a choice between many elements contained in
an
xs:choice
element such as within
name
(line 31‑). Attributes are defined
after
the definitions of the elements in sequence or in choice even though they appear in the
start-tag (see
code
as attribute of
wine
,
line 27‑).
xs:choice
and
xs:sequence
can be nested. For example,
name
(line 31‑) indicates a choice between three elements of type
string
first
,
family
and
initial
, which can be repeated any number of times. Indeed, because an
element only occurs once by default (i.e.
minOccurs="1"
) and that
maxOccurs="unbounded"
, each element can appear as often as we wish.
An existing element can also be referred to using the
ref
attribute like
name
used in
Owner
(line 84‑). But be aware that, in this case, if you had
mistakenly used the
name
attribute instead of
ref
, you would have named a new element with no connection with the one you
wished to have referenced; this can lead to errors that are difficult to track down (I
learned this the hard way!!!).
In
Example 3.4, the
mixed="true"
attribute in the definition of a type (see
Comment
,
line 103‑) means that character data can also appear between
the elements described by the content of the type. In this case, character data can thus
be interspersed with any number of
emph
and
bold
elements.
We have introduced namespaces for instance documents in Section 2.1, but they only show their full power during the validation process in which the combination of element names and namespaces must correspond between the instance and the schema. Of course, namespaces must be properly combined during file inclusion and details can become quite intricate.
We will illustrate a simple but quite frequent case with Example 3.3
and Example 3.4
. These schemas both define a
wine
element, but with meanings and contents which must be distinguished. This is achieved
with namespace declarations. A similar name clash occurs when we must define an element
called
type
or
sequence
that is already used in the schema vocabulary. This is why we define a namespace
(usually
xs
or
xsd
) for the element names of an XML Schema.
By default, names without prefixes are defined in the
empty namespace
or the namespace assigned to the
xmlns
attribute. To create elements in a specific namespace (and not the empty one), we set a
value for the attribute as it is done for element
targetNamespace
in
Example 3.4
(line 6‑). The same namespace is also assigned to
the prefix
cat
.
elementFormDefault
is set to
qualified
(line 3‑
of Example 3.4) so that global elements
and types of an included file are visible in the including file. The
attributeFormDefault
is set to
unqualified
to ensure that the attributes are in the same namespace as the containing element.
The import of elements from an external schema file along with its namespaces is done
using
xs:import
(line 5‑
of Example 3.3) indicating the namespace used in this
file for the target namespace of the imported file (here we keep the same one) and the
location of the file to be imported. The imported namespace must be given a prefix
definition with an
xmlns
declaration, see the root tag of
Example 3.3. Because the name associated with the target namespace
of the imported file is the same as the one associated with the
cat
prefix, we use
cat:wine
to refer to the
wine
element of
Example 3.4. Namespace and importation of RELAX NG schemas,
described in the next section, are similar in principle to what we have shown for the
importation of XML Schema.
We now better understand how namespaces are used in the
instance documents
and how they are linked to their schemas. For example, the first lines of
Example 2.3
define the namespace associated with the
null prefix
(i.e. only the element name) as the value of the
xmlns
attribute. We also indicate the namespace and the location of the Schema to be used for
validation as the value of the
xsi:schemaLocation
. The
xsi
prefix must also be defined by an attribute starting with
xmlns:
. Because all elements defined in this file are in the same package,
to which we have assigned the null prefix, no namespace prefix is used in this file.
Example 2.1
(page )
shows the outline of our XML instance files validated by the two XML Schemas described
in this section. Their outline is shown in
Example 3.5. The instance file of the wine catalog is included in the
one of the cellar book. So it was natural and convenient to have a similar organization
for the corresponding schemas even though this is not always the case. In this case, the
wine catalog schema is included in the cellar book schema. Note the use of the namespace
prefixes in both the XML instance and the corresponding XML Schema files. The part in
bold characters in
Example 2.1
and
Example 3.5
belong to different namespaces. Here, we kept the same name for the namespaces in both
the instance file of the wine catalog and the corresponding schema. In
Example 3.5, we see that references to lines in bold need to use the
namespace prefix
cat
for the
SAQ-code
type (line 37‑) and the
wine-catalog
element (line 43‑) .
These examples show another interesting use of namespaces: to make sure that relative
references are kept, an
xml:base
attribute (line 19‑) is added to the root element (line 14‑
)
by when an instance file is included into another. In order that the resulting instance file remains valid,
xml:base
must be added as an attribute in the
WineCatalog.xsd
schema.
xml:base
is itself a special XML type whose definition must also be imported
(line 11‑
of Example 3.5).
Example 3.5. Outline of
CellarBook.xsd
importing
Example 3.4
(shown in italics) in a different namespace.
1 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:cat="http://www.iro.umontreal.ca/lapalme/wine-catalog"> <xs:schema 5 xmlns:xs='http://www.w3.org/2001/XMLSchema' elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:cat ="http://www.iro.umontreal.ca/lapalme/wine-catalog" targetNamespace="http://www.iro.umontreal.ca/lapalme/wine-catalog">10 <xs:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/03/xml.xsd"/> <xs:element name="wine-catalog">
15 <xs:complexType> <xs:sequence minOccurs="0" maxOccurs="unbounded"> <xs:element name="wine" type="cat:Wine"/> </xs:sequence> <xs:attribute ref="xml:base"/>
20 </xs:complexType> </xs:element> <xs:complexType name="Wine">...</xs:complexType>
... 25 </xs:schema> <xs:element name="cellar">
<xs:complexType> <xs:sequence minOccurs="0" maxOccurs="unbounded"> 30 <xs:element name="wine" type="Wine"/>
</xs:sequence> </xs:complexType> </xs:element> 35 <xs:complexType name="Wine">
<xs:sequence>...</xs:sequence> <xs:attribute name="code" type="xs:IDREF" use="required"/>
</xs:complexType> 40 <xs:element name="cellar-book">
<xs:complexType> <xs:sequence> <xs:element ref="cat:wine-catalog"/>
<xs:element name="owner" type="Owner"/> 45 <xs:element name="location" minOccurs="0">...</xs:element> <xs:element ref="cellar"/> </xs:sequence> </xs:complexType> </xs:element> 50 ... </xs:schema>
|
The target namespace of the imported schema is the one specified by
the
|
|
Imports another schema whose content is shown here in italics. |
|
The wine catalog is a list of wine descriptions. |
|
Ensures that the
|
|
Type for the wine description in the catalog. |
|
Definition of the cellar in the top-level schema. |
|
|
|
Type for the wine description in the cellar. |
|
Reference to the imported type for the wine code. |
|
Top-level element of the cellar. |
|
Reference to the imported type for the wine-catalog (line 14‑ |