[This local archive copy is from the official and canonical URL, http://www.w3.org/TandS/QL/QL98/pp/microsoft-serializing.html, 1999-02-05; please refer to the canonical source document if possible.]


XML Syntax Recommendation for Serializing Graphs of Data

author: Andrew Layman (andrewl@microsoft.com), December 2, 1998.

This paper describes a specific way to use XML in order to serialize graphs of data such as database tables and relations, nodes and edges from directed labeled graphs, and similar constructions. By graphs, we mean objects having properties and relations to other objects, where the relations are directed (and have inverses) and where there may be multiple paths to an object. A graph of data serialized according to the described rules is said to be in "canonical form." Other representations of the same data can be mapped into and out of the canonical form.

This paper does not change the fact that every validatable XML document conforms to a specific grammar. Rather, it proposes a way to mechanically generate, from a database's or graph's schema, a particular grammar that can be used to serialize data from the database or graph, and into which any other serialization of that data can be mapped.

When designing the canonical form, the following criteria were considered:

  1. Typical syntax must be readable by humans.
  2. It must use only facilities available today in XML, that is, the XML 1.0 specification plus the XML Namespaces specification.
  3. The rules must be simple enough to be easily taught.
  4. Instances must be capable of expressing graphs of objects and directed relations.
  5. The syntax must support a clear query model.
  6. It should be possible to embed the syntax within web pages. It is beneficial if such embedded XML does not affect rendering.
  7. There must be a well-defined mechanism for mapping other syntax families to and from canonical form.

It is not a requirement that all serializations must be fully decodable without schema. That is, while it is beneficial if basic information can be extracted from a document lacking its schema, it is acceptable if full decoding requires schema.

Canonical syntax is syntax which obeys these five rules:

  1. Entities (nodes) are expressed as elements.
  2. Properties (edges) are expressed as attributes.
  3. Relations (edges) to other objects are expressed as attributes with datatype of "IDREF". If several objects are related by the same relation type, they are expressed as a single attribute with datatype "IDREFS".
  4. The top-level element is the name of a package or message type. All other elements are child elements of that top-level element (except as noted by rule 5, below). Order does not matter.
  5. If an element can only be related to one other element, and cannot exist independently, it may be expressed as a child of either the top-level element or that single other element.

For example, consider a database or other graph (described by a UML diagram or other notation) and containing

A serialized instance would look like

<School>
  <Class id="Class:19" name="Western Civilization" taughtBy="#Teacher83"/>
  <Class id="Class:253" name="English Literature" taughtBy="#Teacher83"/> 
  <Student id="Student:30006" name="Raphael" home="Address:1" attends="Class:19">
    <Address id="Address:1" street="950 Greenhill Rd" city="Mill Valley" state="CA"/>
  </Student>
  <Student id="Student:2567" name="Michael" home="Address:3" attends="Class:19 Class:253">
    <Address id="Address:3" street="28 Mountain Road" city="Lark Creek" state="CA"/>
  </Student>
  <Student id="Student:31415" name="Sandro" home="Address:4" attends="Class:253">
    <Address id="Address:4" street="14 16 Street" city="San Raphael" state="CA"/>
  </Student>
  <Teacher id="Teacher:83" name="Thorsten">
</School>

Entities may have relations to entities not in the serialized graph using the same general mechanism, but where the attribute's datatype is "uri". For example:

<Student id="Student:31415" name="Linda" webPage="http://home.navisoft.com/lindamann"/>

As mentioned earlier, if several entities are related by the same relation type, they are expressed as a single attribute with datatype "IDREFS". The order in which the ids are listed is presumed significant, and expresses the ordering (if any) of the collection of related entities (e.g. chapters in a book). When significant, it is fundamentally an aspect of the relations between the elements (e.g. between the chapters, such that chapter 1 precedes chapter two, and so on).

This does not preclude application domains designing vocabulary for collections with more specialized semantics. In these cases, the semantics would be indicated by explicit collection elements, or by information in the schema for the relation attribute, as appropriate. Similarly, while these rules permit the serialization of any graph, they neither include nor preclude elements or attributes with specific semantics, including elements or attributes designed to layer on additional graph facilities such as reference, attribution or subsumption. All of these facilities can be effected by designing appropriate vocabularies and namespaces.

Procedure for UML to Syntax Schema conversion

Given an arbitrary UML diagram, we can mechanically produce a canonical grammar.

  1. Objects are expressed as elements. They always have id attributes.
  2. Properties are expressed as attributes.
  3. Relations are expressed as attributes. The value of the attribute is an idref (or space-separated list of idrefs) to the related element. (Relations to objects not in the serialized instance have datatype uri or uris.)
  4. The top-level element is the name of the package or message.
  5. The top-level element has a content model which allows any other element type in any order.
  6. If an object can only be referenced once, and its existence is dependent on the existence of another element, it may also appear in the content model of the referencing element (which continues to have an attribute making the relation explicit).
  7. Regarding ordering, content models use a group that allows sub-elements to appear in any order.
  8. A relation between elements could be expressed as an attribute of either side. To choose which side gets the content,
    1. If only one role is named, use that, else
    2. Pick the role with the smallest maximum cardinality, else
    3. Pick the role with the largest minimum cardinality, else
    4. Pick the role with the shortest name, else
    5. Pick the role whose name appears first in the alphabet.

Procedure for Graph to XML Instance Conversion

  1. Emit the top-level element tag corresponding to the package or message. Within this,
  2. Walk the graph using any of the well-known techniques. For each node,
    1. Emit a element corresponding to the node, with GI indicating the node's type and with a unique id attribute/value. Emit attributes corresponding to each property and relation, where the value of a relation is expressed as a idref if the object of the relation is in the graph, else as a full uri. If order of related nodes is significant, emit relations in that order.
    2. Optionally, if the object of a relation is known to be only potentially referred to by a single node, emit that object node as a child element, following these rules recursively. Else defer to later in the graph walk.

Procedure for XML Instance to Graph Conversion

  1. For each element in the document, create a node identified by the id attribute of the element, and with a node type given by the type of the element.
  2. For each relation (idref or idrefs) attribute of each element create an edge whose role name is identified by the attribute's name and whose value is the node identified by the attribute's value.
  3. For each property attribute of each element create a property whose role name is identified by the attribute's name and whose value is the attribute's value. The type of the value is identified by the datatype of the attribute.

Procedure for converting a set of Database Tables to XML Instance Conversion

  1. Emit the top-level element tag corresponding to the package or message. Within this,
  2. For each row in each table
    1. Emit a element corresponding to the row. The id attribute has a value formed by concatenating the element type name with the value of the primary key column, separating with a colon. (Multi-column keys should be concatenated into a single key value, using a separator that allows the parts to be separated again.) Emit attributes corresponding to each property, formatting as necessary according to the column's datatype. Emit attributes corresponding to each relation, where the value of a relation is expressed as an idref if the object of the relation is in the instance, else as a full uri. . If order of related rows is significant, emit relations in that order.
    2. Optionally, if the object of a relation is known to be only potentially referred to by this single row, emit that object as a child element, following these rules recursively. Else defer to later in the output.

Procedure for XML Instance to set of Database Tables Conversion

  1. For each element type, create a table with columns corresponding to the attributes of the element type.
  2. For each element in the document, create a row in the table corresponding to the element's name, with the row identified by its id value.
  3. For each property attribute of each element, set the corresponding column to have a value equal to the attribute's value., decoding it if necessary according to the datatype.
  4. For each relation (idref or idrefs) attribute of each element, set the corresponding column to have a (foreign key) value equal to the primary key attribute of the referenced element. (Multi-column keys are more complicated. If the element contains the attributes corresponding to the foreign key columns, the row will reference the right other row. But if it does not, extra information will be needed to know which PK column corresponds to which FK column.)

Mapping Abbreviated Syntax to Canonical Syntax

A fully-explicit, canonical syntax makes it easy to convert from syntax to a graph of objects. Provided one has a schema telling which attributes are IDREFs, one merely interprets all attributes as either properties or relations via IDREF. However, the canonical syntax is not the only syntax that could be used to serialize a graph. In many cases, alternative syntaxes may be used, either due to historical or political factors, or to take advantage of compressions that are available if one has domain knowledge. We call all of these "abbreviated syntaxes." For example, we might find an instance such as this:

<Class>
  <name>Western Civilization</name>
  <taughtBy>Thorsten</taughtBy>
  <attendedBy>Raphael</attendedBy>
  <attendedBy>Smith</attendedBy>
</Class>

Here, the class's name was expressed by a sub-element, and teachers and students were identified only by their name. We need a means to convert such abbreviated syntax to a fully-explicit (canonical) syntax. There are two basic approaches possible. One is to have some declarative information in the schema that restores the missing elements. The other is to use a transform language such as XSL to convert the abbreviated to a explicit syntax.

The declarative approach is initially simpler. each abbreviated syntactic schema declares its relation to a canonical schema and provides appropriate declarative mappings. The drawback to this is that it requires additions to the schema vocabulary, and can only handle a limited number of simple cases. In the real world, judging by the experience with Architectural Forms, especially given the deployment of systems that evolve over several years, declarative mapping either fails or becomes very complex.

If we take the transform language approach, then each abbreviated syntactic schema declares its relation to a canonical schema and provides appropriate transforms to and from.

We right now favor a composite approach. For a small number of very common and simple cases we can annotate schemas with declarative mapping information in the form of attributes of the element types. The exact details of what constitutes "common and simple" should be determined, but candidates appear to be (a) simple renaming of elements or attributes, (b) conversion of a sub-element to an attribute, (c) inference of a relation based on element containment, (d) reference by a "foreign key" converted to reference by IDREF or URI. For more complex cases we should look to a transform language such as XSL.

Finally, one might reasonably ask why we have a canonical syntax at all. Why not provide mappings directly to the graph's schema? But if we ask that, we need to also ask what those mappings would look like. In effect, they would map elements and attributes to objects and properties, much as XSL maps things today, but using new keywords to signal the difference in result types. Having done all that – introducing a new vocabulary for syntax to graph mapping – we would not have any greater functionality than provided by the canonical syntax approach, but we would have doubled the vocabulary needed. Further, we would require that all clients of XML implement mapping machinery (while with the canonical syntax approach a server could choose to emit canonical syntax, thereby avoiding any need for a special mapper). We would not be able to leverage future developments in XSL Finally, we would not be providing any clear suggestions for syntax that people should use, and would therefore greatly increase the actual amount of mapping that would need to occur.

Appendix A: Schema for Classes, Students, Teachers example

This sample schema uses XML-Data notation to describe a vocabulary and syntax for serializing the example data of Classes, Students and Teachers.


<?xml version="1.0" encoding="windows-1252" ?>
<!-- Schema for package ClassesStudentsTeachers  -->
<Schema   xmlns="urn:schemas-microsoft-com:xml-data"
            xmlns:dt="urn:schemas-microsoft-com:datatypes"
            xmlns:x="urn:schemas-microsoft-com:xml-data-ex">
 
 
    <!-- *****  TYPE Address ***** -->
 
 
    <ElementType name="Address">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
 
    </ElementType>
 
 
    <!-- *****  TYPE Class ***** -->
 
 
    <ElementType name="Class">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
  
        <AttributeType name="name" dt:type="string"/>
            <attribute type="name" required=&apos;yes&apos; />         
        <AttributeType name="taughtBy" dt:type="idref" />
            <attribute type="taughtBy" required=&apos;yes&apos; x:range="Teacher"/>
 
    </ElementType>
 
 
    <!-- *****  TYPE Student ***** -->
 
 
    <ElementType name="Student">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
         
        <AttributeType name="attends" dt:type="idrefs" />
            <attribute type="attends" x:range="Class"/>         
        <AttributeType name="home" dt:type="idref" />
            <attribute type="home" x:range="Address"/> 
        <AttributeType name="name" dt:type="string"/>
            <attribute type="name" required=&apos;yes&apos; />
 
        <group seq=&apos;many&apos;>
            <element  type="Address" minOccurs="0" maxOccurs="1" />
        </group>
 
    </ElementType>
 
 
    <!-- *****  TYPE Teacher ***** -->
 
 
    <ElementType name="Teacher">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="name" dt:type="string"/>
            <attribute type="name" required=&apos;yes&apos; /> 
 
    </ElementType>
 
  
  <!-- The PACKAGE -->
 
 
 
 
    <!-- *****  TYPE School ***** -->
 
 
    <ElementType name="School">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
         
        <AttributeType name="classes" dt:type="idrefs" />
            <attribute type="classes" x:range="Class"/>         
        <AttributeType name="students" dt:type="idrefs" />
            <attribute type="students" x:range="Student"/>         
        <AttributeType name="teachers" dt:type="idrefs" />
            <attribute type="teachers" x:range="Teacher"/>
 
        <group seq=&apos;many&apos;>
            <element  type="Student" minOccurs="0" maxOccurs="*" />
            <element  type="Class" minOccurs="0" maxOccurs="*" />
            <element  type="Teacher" minOccurs="0" maxOccurs="*" />
        </group>
 
    </ElementType>
 
 
</Schema>

Appendix B: Schema for the "Northwind" Sample Database

The "Northwind" database is a sample database supplied with Microsoft Access, containing representative tables for a hypothetical business.

<?xml version="1.0" encoding="windows-1252" ?>
<!-- Schema for package Northwind Database  -->
<Schema   xmlns="urn:schemas-microsoft-com:xml-data"
            xmlns:dt="urn:schemas-microsoft-com:datatypes"
            xmlns:x="urn:schemas-microsoft-com:xml-data-ex">
 
 
    <!-- *****  TYPE Category ***** -->
 
 
    <ElementType name="Category">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="categoryID" dt:type="string" x:type="int"/>
            <attribute type="categoryID" required=&apos;yes&apos; /> 
        <AttributeType name="categoryName" dt:type="string"/>
            <attribute type="categoryName" required=&apos;yes&apos; />  
        <AttributeType name="description" dt:type="string"/>
            <attribute type="description" required=&apos;yes&apos; />
 
    </ElementType>
 
 
    <!-- *****  TYPE Customer ***** -->
 
 
    <ElementType name="Customer">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="address" dt:type="string"/>
            <attribute type="address" required=&apos;yes&apos; /> 
        <AttributeType name="city" dt:type="string"/>
            <attribute type="city" required=&apos;yes&apos; /> 
        <AttributeType name="companyName" dt:type="string"/>
            <attribute type="companyName" required=&apos;yes&apos; /> 
        <AttributeType name="contactName" dt:type="string"/>
            <attribute type="contactName" required=&apos;yes&apos; /> 
        <AttributeType name="contactTitle" dt:type="string"/>
            <attribute type="contactTitle" required=&apos;yes&apos; /> 
        <AttributeType name="country" dt:type="string"/>
            <attribute type="country" required=&apos;yes&apos; /> 
        <AttributeType name="customerID" dt:type="string"/>
            <attribute type="customerID" required=&apos;yes&apos; /> 
        <AttributeType name="fax" dt:type="string"/>
            <attribute type="fax" required=&apos;yes&apos; />  
        <AttributeType name="phone" dt:type="string"/>
            <attribute type="phone" required=&apos;yes&apos; /> 
        <AttributeType name="postalCode" dt:type="string"/>
            <attribute type="postalCode" required=&apos;yes&apos; />
 
    </ElementType>
 
 
    <!-- *****  TYPE Employee ***** -->
 
 
    <ElementType name="Employee">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="address" dt:type="string"/>
            <attribute type="address" required=&apos;yes&apos; /> 
        <AttributeType name="birthDate" dt:type="string" x:type="date"/>
            <attribute type="birthDate" required=&apos;yes&apos; /> 
        <AttributeType name="city" dt:type="string"/>
            <attribute type="city" required=&apos;yes&apos; /> 
        <AttributeType name="country" dt:type="string"/>
            <attribute type="country" required=&apos;yes&apos; /> 
        <AttributeType name="employeeID" dt:type="string" x:type="int"/>
            <attribute type="employeeID" required=&apos;yes&apos; /> 
        <AttributeType name="firstName" dt:type="string"/>
            <attribute type="firstName" required=&apos;yes&apos; /> 
        <AttributeType name="hireDate" dt:type="string" x:type="date"/>
            <attribute type="hireDate" required=&apos;yes&apos; /> 
        <AttributeType name="homePhone" dt:type="string"/>
            <attribute type="homePhone" required=&apos;yes&apos; /> 
        <AttributeType name="lastName" dt:type="string"/>
            <attribute type="lastName" required=&apos;yes&apos; />  
        <AttributeType name="notes" dt:type="string"/>
            <attribute type="notes" required=&apos;yes&apos; /> 
        <AttributeType name="postalCode" dt:type="string"/>
            <attribute type="postalCode" required=&apos;yes&apos; /> 
        <AttributeType name="region" dt:type="string"/>
            <attribute type="region" required=&apos;yes&apos; />         
        <AttributeType name="reportsTo" dt:type="idref" />
            <attribute type="reportsTo" required=&apos;yes&apos; x:range="Employee"/> 
        <AttributeType name="title" dt:type="string"/>
            <attribute type="title" required=&apos;yes&apos; /> 
        <AttributeType name="titleOfCourtesy" dt:type="string"/>
            <attribute type="titleOfCourtesy" required=&apos;yes&apos; /> 
 
    </ElementType>
 
 
    <!-- *****  TYPE Order ***** -->
 
 
    <ElementType name="Order">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
         
        <AttributeType name="customer" dt:type="idref" />
            <attribute type="customer" required=&apos;yes&apos; x:range="Customer"/>          
        <AttributeType name="employee" dt:type="idref" />
            <attribute type="employee" required=&apos;yes&apos; x:range="Employee"/> 
        <AttributeType name="freight" dt:type="string" x:type="float"/>
            <attribute type="freight" required=&apos;yes&apos; /> 
        <AttributeType name="orderDate" dt:type="string" x:type="date"/>
            <attribute type="orderDate" required=&apos;yes&apos; /> 
        <AttributeType name="orderID" dt:type="string" x:type="int"/>
            <attribute type="orderID" required=&apos;yes&apos; /> 
        <AttributeType name="requiredDate" dt:type="string" x:type="date"/>
            <attribute type="requiredDate" required=&apos;yes&apos; /> 
        <AttributeType name="shipAddress" dt:type="string"/>
            <attribute type="shipAddress" required=&apos;yes&apos; /> 
        <AttributeType name="shipCity" dt:type="string"/>
            <attribute type="shipCity" required=&apos;yes&apos; /> 
        <AttributeType name="shipCountry" dt:type="string"/>
            <attribute type="shipCountry" required=&apos;yes&apos; /> 
        <AttributeType name="shipName" dt:type="string"/>
            <attribute type="shipName" required=&apos;yes&apos; /> 
        <AttributeType name="shippedDate" dt:type="string" x:type="date"/>
            <attribute type="shippedDate" required=&apos;yes&apos; /> 
        <AttributeType name="shipPostalCode" dt:type="string"/>
            <attribute type="shipPostalCode" required=&apos;yes&apos; /> 
        <AttributeType name="shipRegion" dt:type="string"/>
            <attribute type="shipRegion" required=&apos;yes&apos; />         
        <AttributeType name="shipVia" dt:type="idref" />
            <attribute type="shipVia" required=&apos;yes&apos; x:range="Shipper"/>
 
    </ElementType>
 
 
    <!-- *****  TYPE OrderDetail ***** -->
 
 
    <ElementType name="OrderDetail">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="discount" dt:type="string" x:type="float"/>
            <attribute type="discount" required=&apos;yes&apos; />         
        <AttributeType name="order" dt:type="idref" />
            <attribute type="order" required=&apos;yes&apos; x:range="Order"/>         
        <AttributeType name="product" dt:type="idref" />
            <attribute type="product" required=&apos;yes&apos; x:range="Product"/> 
        <AttributeType name="quantity" dt:type="string" x:type="float"/>
            <attribute type="quantity" required=&apos;yes&apos; /> 
        <AttributeType name="unitPrice" dt:type="string" x:type="float"/>
            <attribute type="unitPrice" required=&apos;yes&apos; />
 
    </ElementType>
 
 
    <!-- *****  TYPE Product ***** -->
 
 
    <ElementType name="Product">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
         
        <AttributeType name="category" dt:type="idref" />
            <attribute type="category" required=&apos;yes&apos; x:range="Category"/> 
        <AttributeType name="discontinued" dt:type="string" x:type="boolean"/>
            <attribute type="discontinued" required=&apos;yes&apos; /> 
        <AttributeType name="productID" dt:type="string" x:type="int"/>
            <attribute type="productID" required=&apos;yes&apos; /> 
        <AttributeType name="productName" dt:type="string"/>
            <attribute type="productName" required=&apos;yes&apos; />  
        <AttributeType name="quantityPerUnit" dt:type="string"/>
            <attribute type="quantityPerUnit" required=&apos;yes&apos; /> 
        <AttributeType name="reorderLevel" dt:type="string" x:type="int"/>
            <attribute type="reorderLevel" required=&apos;yes&apos; />         
        <AttributeType name="supplier" dt:type="idref" />
            <attribute type="supplier" required=&apos;yes&apos; x:range="Supplier"/> 
        <AttributeType name="unitPrice" dt:type="string" x:type="float"/>
            <attribute type="unitPrice" required=&apos;yes&apos; /> 
        <AttributeType name="unitsInStock" dt:type="string" x:type="int"/>
            <attribute type="unitsInStock" required=&apos;yes&apos; /> 
        <AttributeType name="unitsOnOrder" dt:type="string" x:type="int"/>
            <attribute type="unitsOnOrder" required=&apos;yes&apos; />
 
    </ElementType>
 
 
    <!-- *****  TYPE Shipper ***** -->
 
 
    <ElementType name="Shipper">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="companyName" dt:type="string"/>
            <attribute type="companyName" required=&apos;yes&apos; /> 
        <AttributeType name="phone" dt:type="string"/>
            <attribute type="phone" required=&apos;yes&apos; /> 
        <AttributeType name="shipperID" dt:type="string" x:type="int"/>
            <attribute type="shipperID" required=&apos;yes&apos; />
 
    </ElementType>
 
 
    <!-- *****  TYPE Supplier ***** -->
 
 
    <ElementType name="Supplier">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
 
        <AttributeType name="address" dt:type="string"/>
            <attribute type="address" required=&apos;yes&apos; /> 
        <AttributeType name="city" dt:type="string"/>
            <attribute type="city" required=&apos;yes&apos; /> 
        <AttributeType name="companyName" dt:type="string"/>
            <attribute type="companyName" required=&apos;yes&apos; /> 
        <AttributeType name="contactName" dt:type="string"/>
            <attribute type="contactName" required=&apos;yes&apos; /> 
        <AttributeType name="contactTitle" dt:type="string"/>
            <attribute type="contactTitle" required=&apos;yes&apos; /> 
        <AttributeType name="country" dt:type="string"/>
            <attribute type="country" required=&apos;yes&apos; /> 
        <AttributeType name="fax" dt:type="string"/>
            <attribute type="fax" required=&apos;yes&apos; /> 
        <AttributeType name="homePage" dt:type="string" x:type=""/>
            <attribute type="homePage" required=&apos;yes&apos; /> 
        <AttributeType name="phone" dt:type="string"/>
            <attribute type="phone" required=&apos;yes&apos; /> 
        <AttributeType name="postalCode" dt:type="string"/>
            <attribute type="postalCode" required=&apos;yes&apos; /> 
        <AttributeType name="region" dt:type="string"/>
            <attribute type="region" required=&apos;yes&apos; /> 
        <AttributeType name="supplierID" dt:type="string" x:type="int"/>
            <attribute type="supplierID" required=&apos;yes&apos; /> 
 
    </ElementType>
 
  
  <!-- The PACKAGE -->
 
 
 
 
    <!-- *****  TYPE Nwind ***** -->
 
 
    <ElementType name="Nwind">
 
        <AttributeType name="id" dt:type="id"/>
            <attribute type="id" />
         
        <AttributeType name="categories" dt:type="idrefs" />
            <attribute type="categories" x:range="Category"/>         
        <AttributeType name="customers" dt:type="idrefs" />
            <attribute type="customers" x:range="Customer"/>         
        <AttributeType name="employees" dt:type="idrefs" />
            <attribute type="employees" x:range="Employee"/>         
        <AttributeType name="orderDetails" dt:type="idrefs" />
            <attribute type="orderDetails" x:range="OrderDetail"/>         
        <AttributeType name="orders" dt:type="idrefs" />
            <attribute type="orders" x:range="Order"/>         
        <AttributeType name="products" dt:type="idrefs" />
            <attribute type="products" x:range="Product"/>         
        <AttributeType name="shippers" dt:type="idrefs" />
            <attribute type="shippers" x:range="Shipper"/>         
        <AttributeType name="suppliers" dt:type="idrefs" />
            <attribute type="suppliers" x:range="Supplier"/>
 
        <group seq=&apos;many&apos;>
            <element  type="Order" minOccurs="0" maxOccurs="*" />
            <element  type="Customer" minOccurs="0" maxOccurs="*" />
            <element  type="Employee" minOccurs="0" maxOccurs="*" />
            <element  type="Supplier" minOccurs="0" maxOccurs="*" />
            <element  type="Category" minOccurs="0" maxOccurs="*" />
            <element  type="Product" minOccurs="0" maxOccurs="*" />
            <element  type="OrderDetail" minOccurs="0" maxOccurs="*" />
            <element  type="Shipper" minOccurs="0" maxOccurs="*" />
        </group>
 
    </ElementType>
 
 
</Schema>