Mapping W3C Schemas to Object Schemas to Relational Schemas

Copyright March, 2001 by Ronald Bourret

1.0 Introduction

This paper summarizes two different mappings. The first part of the process, generally known as XML data binding, maps the W3C's XML Schemas to object schemas. The second, known as object-relational mapping, maps object schemas to relational database schemas.

The two mappings can be joined (and the intermediate object schema eliminated) to create a mapping from XML Schemas to database schemas. This is not shown, but left as an exercise to the reader. Note that because individual XML Schema structures can often be mapped to multiple object structures, and because individual object structures can often be mapped to multiple database structures, there are usually multiple possible mappings from XML Schemas to database schemas.

This paper is based on the March 16, 2001 proposed recommendation of XML Schema Part 1: Structures.

1.1 A Quick Conclusion

Although it might sound odd, especially after having written this paper, I am relatively convinced that mapping XML Schemas to object schemas is not a good idea if it can be avoided. Mapping an XML Schema to an object schema implies that an application will use data-specific objects to manipulate the data in documents described by the schema. In such cases, the documents are of no inherent interest. That is, they are used solely as a data transport. (If the application was interested in the documents themselves, it would use the DOM to access the documents, not data-specific objects.)

If an application is interested in the data in a document, the obvious question is, "Why is the data being described with XML Schemas?" XML Schemas are designed to model XML documents, not data, and data is better modeled in an object schema or a design language such as UML. Thus, the more interesting mapping for such applications is from object schemas or UML to XML Schemas. This allows the application to model its data in a language designed for that purpose, then map the design to XML Schemas so that XML documents can be used as a transport for that data. Furthermore, although I have not looked at the object schema to XML Schema mapping in detail, it appears to be simpler than the reverse mapping because object schemas are less rich than XML Schemas.

That said, the mapping from XML Schemas to object schemas is often necessary. The most common cause of this is that you would like to store the data from documents that conform to a specific XML dialect (such as RosettaNet, Commerce One, or Ariba) in a database. These dialects define a serialization format, but no underlying object model. Thus, it is up to each application that interprets these documents to map the serialization format to its own object model and database schema.

Whether it is a good idea to define a serialization format without an underlying object model is, in itself, a good question. In a closed system, the answer is probably "no", for the reasons stated above -- data models are better designed in something like UML. In an open system, the answer is probably "yes". The reason is that, in open systems, most participants in the system probably have their own models already and trying to impose a common object model is likely to fail, for political as well as technical reasons. Thus, a common serialization format, from which individual participants can populate their own objects, is a reasonable compromise.

For further discussion of some of the issues here, see the thread "building an object model of a XML schema" in the archives of the xml-dev mailing list for July, 2001.

A number of projects have mapped XML Schemas to object schemas. Among these are Sun's Java Architecture for XML Binding, also known as Project Adelard, XML Data Binding, and JSR-31, the systems apparently developed by Commerce One / Veo Systems (alluded to in the SOX specification), Castor, Quick, and the Zeus project in Enhydra.

The mapping presented here is probably similar to the first two (no public details of those projects are available) and appears to be a superset of the latter three. In fact, my reason for presenting this work is solely that none of these projects has made a complete mapping from XML Schemas to object schemas publicly available.

For URLs and brief descriptions of XML data binding products, as well as a list of papers about XML data binding, see XML Data Binding.

1.3 Feedback

Please send any feedback on this paper to me at rpbourret@rpbourret.com.

2.0 About this Paper

This paper describes the mapping in terms of the data model presented in XML Schemas Part 1: Structures, rather than the XML syntax used to describe schemas. Although I might eventually add a section describing the mapping based on the XML syntax, this is currently left as a (non-trivial) exercise for the reader.

This paper is not intended to be a tutorial for the mapping process and is therefore rather terse. For a more complete description of the mapping process, see Mapping DTDs to Databases". Although that presentation is based on DTDs and does not describe the complete mapping described here, the concepts involved are the same.

The purpose of this paper is to help people write code that can automatically generate object and database schemas from XML Schemas, as well as transferring data between XML documents, objects, and databases according to mappings between them. Because the set of possible mappings from XML Schemas to object schemas is fairly large, I do not expect any software to support all possible mappings any time soon, if ever. A more reasonable strategy is for the software to pick a subset of mappings that make sense for its uses and implement those.

2.1 Classes

Although the term "classes" is used throughout, the classes referred to here are closer to C structs than C++ or Java classes. This is because these classes have no behavior (methods) -- they only have properties. The reason for this is that XML and databases do not have behavior -- they only have data and structure.

Furthermore, other structured types, such as interfaces in Java, may be used instead of classes. In such cases, the corresponding parts of the structure, such as accessor / mutator pairs, are used instead of properties. Whether such mappings make sense depends on the node. For example, it probably makes no sense to map a concrete element type to an interface, but might make sense to map an abstract element type to an interface. The rest of this paper describes the mapping in terms of classes and properties, but should be understood as including other structured types as well.

2.2 "Nullable" properties and accessors / mutators

This paper uses the concept of "nullable" properties, in spite of the fact that object-oriented programming languages don't support these at a conceptual level. A nullable property is one which can indicate that the property value is missing, like a NULL in a database. How this is implemented depends on the language. For example, this can be implemented in Java by always using reference types for properties (e.g. Integer instead of int); this allows the property to be set to null. Another possibility is to provide a parallel boolean property that indicates whether the data property is null.

If structures other than classes are used, nullability is interpreted correspondingly. For example, a nullable accessor in an interface is one that can indicate that the accessed value is null and a nullable mutator is one that can accept a null value. Again, there are multiple possible implementations. For example, an accessor in Java can return a reference to an object (which may be null) or it may have a parallel method that can be called to determine if the accessed value is null.

2.3 Definitions (declarations) v. concepts v. instances

One of the confusing things about working with DTDs and XML schemas is the frequent overlaps in terminology. For example, consider the following:

<!ELEMENT foo EMPTY>
<foo />

The first statement defines the foo element type. The second shows an instance of this element type -- that is, a foo element. The terminology involved here is quite clear. An element type declaration (a physical statement) defines an element type (a conceptual entity), and an instance of an element type is an element (a concrete entity).

Now consider the following:

<ATTLIST foo bar CDATA #REQUIRED>
<foo bar="baz" />

The first statement defines the bar attribute while the second shows an instance of the bar attribute. Note that the same word -- attribute -- is used in both cases. That is, the terminology used to describe attributes does not have an equivalent of element type. This makes it difficult to distinguish between conceptual entities and concrete entities.

The XML Schema specification exacerbates this problem. While most of us think in terms of concepts (rather than definitions of those concepts), the XML Schema data model is couched in terms of declarations and definitions. For example, the data model includes an element declaration component (a component that describes an element declaration) rather than an element type component (a component that describes an element type).

The distinction is subtle, but important, as it would be incorrect to state that an element declaration is mapped to a class. (In fact, an element declaration is mapped to a class declaration.) Because of this, and because of ambiguous terms used in the data model itself -- for example, the nullable property of the element declaration component refers to the nullability of the element type being declared, not the declaration itself -- this paper pretends that the schema data model is, in fact, presented as a model of concepts.

In general, this leads to less cumbersome language and is likely to be closer to the way most people think. It does, however, require occasional interpretation on the part of the reader. For example, when mapping the type definition property of the element declaration component, it is important to remember that this paper describes how to map the type, not the definition.

3.0 General notes on the mapping

3.1 Mapping names

It does not matter whether a component is declared locally or globally. The combination of name and target namespace -- the latter is null for local declarations -- must be mapped to a unique name in the scope to which it is mapped. (Note that in some cases, symbol space must also be considered. For example, an attribute and a local element type in the same complex type can have the same name and target namespace, but must be mapped to separate entities.)

Names may be changed at each step of the mapping. That is, there is no requirement that the same names be used in XML schemas, object schemas, and relational schemas. In fact, renaming is often required because of the shifting boundaries of the spaces in which names exist. For example, an attribute and a simple element in the same complex type can both have the same name, but when they are mapped to properties of the same class, at least one of the names must change. Similarly, the name of a class mapped to a table might collide with an existing table.

Renaming may also be required due to constraints on the length, case, or characters allowed in names in XML (the most liberal), programming structures (generally in the middle), or the database (generally the most restrictive).

3.2 Mapping data types

Scalar data types may be changed at each step of the mapping. That is, an attribute or simple element may have a different data type than their corresponding property and column. Changes in data types are legal only if there is a defined conversion between one data type and the next. Although lossy conversions are theoretically possible, insert semantics for relational databases dictate that all conversions should be lossless. Changing scalar data types is likely to be common due to the differences in type systems in XML schemas, object-oriented programming languages, and databases.

Complex data types may also be changed at each step of the mapping. Such changes can "cast" the data type to more or less complex structures. When it is "cast" to a complex type with fewer properties, the cast is lossy. This has the same effect as simply not mapping the properties that are discarded in the "casting" operation. When the data type is "cast" to a complex type with more properties, the properties to which nothing is mapped must be nullable. Unlike changing scalar data types, changing complex data types is likely to be uncommon.

Finally, it is possible to change from scalar data types to complex data types and vice versa. Mapping a complex data type to a scalar data type requires that it be possible serialize an instance of complex data into a scalar type (such as with XML or in binary form). For example, imagine an XHTML description inside an otherwise data-centric XML document, such as a document describing a book. It is useful to map the XHTML (a description) to a scalar data type (such as, loosely speaking, a String in Java) since the description only is useful as a whole.

Mapping a scalar data type to a complex data type is less common. It requires that the scalar type is mapped to a class with a single property of the type to which the scalar type otherwise would have been mapped. (Why someone would want to do this is not clear.) More useful might be to map the scalar type to another structured type, such as an interface in Java with a single accessor / mutator pair.

3.3 Unmapped structures

There is no requirement that any given structure be mapped at all. The effect of this when transferring data according to the mapping is that instances of unmapped structures, as well as their descendants, are not transferred. (Descendants cannot be transferred because their is no parent structure in which to put them.) For example, if an element is not mapped, then data contained in that element (the element's attributes and content) is not transferred.

3.4 Bidirectionality

The mapping presented here is a mapping from XML schemas to object schemas and object schemas to database schemas. It can be used to transfer data in both directions.

The reverse mappings -- from database schemas to object schema and from object schemas to XML schemas -- are also possible, but are not presented here. At first glance, they appear to be less complex, but I haven't looked closely.

4.0 Mapping XML Schemas to object schemas

The mapping from XML Schemas to object schemas is best understood by viewing an instance of the schema data model as a directed, possibly cyclic graph consisting only of schema, attribute, complex element type, simple element type, attribute group, model group, and wildcard nodes. From this graph, attribute and simple element type nodes are mapped to scalar types. Complex element type, attribute group, and model group nodes are mapped to classes and edges pointing from these nodes are mapped to properties in these classes. Wildcard nodes are generally mapped to arbitrary types, such as Object in Java or pointer to void in C++.

This is best illustrated with an example. Consider the following schema, written here as a DTD for brevity:

   <!ELEMENT A (B, (C | D))>
   <!ATTLIST A
             A1 CDATA #REQUIRED>
   <!ELEMENT B (#PCDATA)>
   <!ELEMENT C (#PCDATA)>
   <!ELEMENT D (C, E)>
   <!ELEMENT E (#PCDATA)>

This can be written as the following graph of schema components

From this graph, it is easy to see that you can construct the following classes:

   class A                  class D
   {                        {
      String a1;               Model_3 m3;
      Model_1 m1;           }
   }

   class Model_1            class Model_3
   {                        {
      String b;                String c;
      Model_2 m2;              String d;
   }                        }

   class Model_2
   {
      String c;
      D d;
   }

There are a number of important things to notice here.

  1. The mapping of model groups is optional. This is particularly useful for model groups pointed to by element types. This eliminates Model_1 and Model_3 in the above mapping and results in the following:
  2.    class A                  class D
       {                        {
          String a1;               String c;
          String b;                String d;
          Model_2 m2;           }
       }
    
       class Model_2
       {
          String c;
          D d;
       }
    

    Other model groups may be eliminated as well. For example, eliminating Model_2 results in the following:

       class A                  class D
       {                        {
          String a1;               String c;
          String b;                String d;
          String c;             }
          D d;
       }
    

    When model groups are eliminated, their properties influence the mapping of the edges on which they occur. For more information, see section 4.7.

  3. Each edge maps to a separate property. This is most apparent in the two edges that point to element type C. Each edge results in a property in the corresponding parent class. Another way of thinking about this is that element types are mapped separately for each content model in which they appear.
  4. When multiple edges point to the same element type (model group, attribute group), there is no requirement that the properties to which the edges map have the same data type. For example, property c in class A could have type String and property c in class D could have type StringBuffer. It is easier to visualize this if the schema is viewed not as a graph but as a tree, where multiple edges pointing to a single element type (model group, attribute group) are portrayed as multiple nodes of that element type (model group, attribute group). This makes it clear that each occurrence of an element type (model group, attribute group) can be mapped to a different data type. (In fact, the description of the schema data model can be read this way.) Although this mapping is possible, it is uncommon.
  5. Particles do not directly correspond to nodes in the graph and are not directly mapped. However, the value of their term property (element type, model group, or wildcard) is used in the graph and the particle is associated with the edge leading from their parent node to the term node. The min occurs and max occurs properties of the particle determine the nullability and cardinality of the property to which the edge is mapped. For more information, see section 4.8.
  6. Although not shown in this example, there is no requirement that attribute groups be mapped. Like model groups, if they are not mapped, their properties influence the mapping of the edges on which they occur. For more information, see section 4.6.
  7. The schema component is used only to determine which nodes must be mapped to global types. That is, any node to which an edge from the schema component points must be mapped to a global type. All other nodes may be mapped to either local or global types. Whether global scalar types are actually declared depends on the language used. For example, Java does not support such types while C++ does. Whether local types are used also depends on the language used. For example, Java supports local complex types in the form of inner classes.
  8. Complex types are problematic and do not quite fit this model. For this reason, they are considered to be transparent in the graph. (They appear sit between complex element type nodes and attribute and particle nodes.) For a complete discussion, see section 4.4.

4.1 Mapping the schema

The schema maps to a set of related programming structures, such as a package in Java. Note that the latest version of the XML Schemas specification does not include a specific schema component in its data model, but the concept is still useful.

4.2 Mapping attributes

Attribute nodes map to (possibly multi-valued) scalar types and edges pointing to attribute nodes map to properties. The following table describes how attribute nodes are mapped to types.

Property Mapping to object-oriented structure
2a. name, target namespace 2a. Type name. Multiple attribute nodes can be mapped to the same type.
2b. type definition 2b. Determines the set of scalar types to which the node can be mapped. For information about how simple types are mapped, see section 4.14.
2c. scope (global) 2c. The scope containing all global types.
2d. scope (complex type) 2d(i). The class to which the complex type is mapped. That is, the type has local scope.
2d(ii). The scope containing all global types.
2e. value constraint (string, default) 2e. Does not apply.
2f. value constraint (string, fixed) 2f. Does not apply.
2g. annotation 2g. A comment in the code.

The following table describes how edges pointing to attribute nodes are mapped to properties.

Property Mapping to object-oriented structure
3a. name, target namespace 3a. Property name. The property name must be unique in the class to which the property belongs.
3b. type definition 3b. Does not apply. The previous table describes how attribute nodes are mapped to types.
3c. scope

3c. Does not apply. All properties are scoped to their parent class, regardless of whether the corresponding attribute declaration was local or global.

Note: Whether the property is visible to other classes is not addressed by this mapping. In general, it seems that the classes would not be useful if the property was not visible, such as by being public or through an accessor / mutator pair.

3d. value constraint (string, default) 3d. Default property value. The default string is converted to the data type of the property.
3e. value constraint (string, fixed) 3e. As in 3d except that the property value cannot be changed. For example, in Java this would use the final qualifier.
3f. annotation 3f. A comment in the code.

4.3 Mapping element types

Simple element type nodes generally map to scalar data types and complex element type nodes generally map to classes. Edges pointing to element type nodes map to properties. The following table describes how element type nodes are mapped to scalar data types or classes.

Property Mapping to object-oriented structure
4a. name, target namespace 4a. Class or scalar type name. Class names must be unique within their scope. Scalar type names are not required to be unique; that is, multiple element type nodes can be mapped to the same scalar type.
4b. type definition (simple) 4b. Determines the set of scalar types to which the node can be mapped. For information about how simple types are mapped, see section 4.14.
4c. type definition (complex) 4c. Determines the set of classes to which the node can be mapped. The properties in the class depend on how descendant nodes are mapped.
4d. scope (global) 4d. The scope containing all global classes.
4e. scope (complex type) 4e(i). The class to which the complex type is mapped. That is, the class has local scope.
4e(ii). The scope containing all global classes.
4f. value constraint 4f. Does not apply.
4g. nillable 4g. Does not apply.
4h. identity constraint definitions 4h. See section 4.10.
4i. substitution group affiliation, substitution group exclusions, disallowed substitutions 4i. See section 4.11.
4j. annotation 4j. A comment in the code.

The following table describes how edges pointing to element type nodes are mapped to properties.

Property Mapping to object-oriented structure
5a. name, target namespace 5a. Property name. The property name must be unique in the class to which the property belongs.
5b. type definition 5b. Does not apply. The previous table describes how element type nodes are mapped to types.
5c. scope

5c. Does not apply. All properties are scoped to their parent class, regardless of whether the corresponding attribute declaration was local or global.

Note: Whether the property is visible to other classes is not addressed by this mapping. In general, it seems that the classes would not be useful if the property was not visible, such as by being public or through an accessor / mutator pair.

5d. value constraint (string, default) 5e. Default property value. The default string is converted to the data type of the property.
5e. value constraint (string, fixed) 5f. As in 5d except that the property value cannot be changed. For example, in Java this would use the final qualifier.
5f. nillable 5g. Whether the property is nullable.
5h. identity constraint definitions 5h. See section 4.10.
5i. substitution group affiliation, substitution group exclusions, disallowed substitutions 5i. See section 4.11.
5j. annotation 5j. A comment in the code.

4.4 Mapping complex types

An element in an XML document is defined by an element type, which is in turn defined (in part) by a complex type. When this chain of relationships is mapped to an object-oriented programming language, an element is mapped to an object, an element type is mapped to a class, and the relationship between an element and an element type is mapped to instantiation. What a complex type is mapped to, and what the relationship between a complex type and an element type is mapped to, are not entirely clear.

Several mappings are possible, but none exactly fits. We will look at each to see whether it preserves the following properties of complex types and their relationships to element types:

Here are the possible mappings of a complex type and the relationship between that complex type and element types, along with the problems of each:

Which mapping to use depends on the language used and the needs of the application. If C++ is used, mapping 4 is probably the best bet. If Java is used, mappings 1 and 3 seem like the best bet. If the mapping to a language is only an intermediate construct in a mapping from schema to database (in which case no objects are ever created), mapping 3 is the best choice. This is because databases don't support the concept of an abstract class (table) and an abstract class can be mapped to a table using the same code is used to map a concrete class, while mapping an interface to a table requires extra code.

The following table describes how complex type definitions are mapped. As was mentioned earlier, complex types appear to sit in the schema graph between element type nodes and attribute group and particle nodes. If this is the case, then the edge pointing from element type to complex type should be mapped according to the chosen mapping of the relationship between element type and complex type. The complex type node is then mapped to a class, and the edges radiating from the node mapped to properties of the class.

Property Mapping to object-oriented structure
6a. name, target namespace 6a. Class name. The combination of name and target namespace must be mapped to a name that is unique in the set of classes to which the schema constructs are mapped. Note that if the complex type does not have a name, it must be given a name when mapped to a class.
6b. base type definition (simple) 6b. Superclass of the class to which the complex type is mapped. This is a class containing a single property of the specified simple type. If the base type definition is the simple ur-type, then the class to which the complex type is mapped can be treated as if it does not inherit from any class (except for required root classes, such as Object in Java).
6c. base type definition (complex) 6c. Superclass of the class to which the complex type is mapped. This is the class to which the base type is mapped. If the base type is the complex ur-type, the class to which the complex type is mapped can be treated as if it does not inherit from any class, except as noted above.
6d. derivation method (extension) 6d. Inheritance.
6e. derivation method (restriction) 6e. This does not appear to have a direct, object-oriented analog. It is a sort of reverse inheritance, but even this is not an exact analogy. It may result in things such as the elimination of properties, changes in the sizes of arrays used for multi-valued properties, or changing a property from nullable to non-nullable.
6f. final (extension) 6f. Final class, that is, a class that may not be extended.
6g. final (restriction) 6g. This does not appear to have a direct, object-oriented analog.
6h. abstract 6h. In some cases, whether the class is abstract. In other cases, cannot be mapped. See the introduction to this section for details.
6i. attribute uses 6i. See section 4.5.
6j. attribute wildcard 6j. See section 4.9.
6k. content type (empty) 6k. Not mapped to any construct. empty is just a flag for whether the complex type definition contains any element type declarations to map.
6l. content type (simple type definition) 6l. A non-nullable, scalar-valued property is added to the class. The type definition determines what property types are legal.
6m. content type (content model, element-only) 6m. The term of the content model (particle) is mapped as specified in sections 4.3, 4.7, and 4.9. The element-only value does not contribute to the mapping.
6n. content type (content model, mixed) 6n. The term of the content model (particle) is mapped as specified in sections 4.3, 4.7, and 4.9. Additionally, an unbounded, multi-valued property capable of holding character strings (e.g. String[ ] in Java) is added to the class.
6o. prohibited substitutions 6o. See section 4.11.
6p. annotations 6p. Comments in the code.

4.5 Mapping attribute uses

Attribute uses are not directly mapped to object schemas. Their attribute declaration property defines an edge from the complex type or attribute group in which they occur and the remaining properties influence how that edge is mapped.

Property Mapping to object-oriented structure
7a. required 7a. Nullability of the property to which the edge is mapped. Specifically, if required is true, the property is non-nullable; if require is false, the property is nullable.
7b. attribute declaration 7b. The attribute node to which the edge points.
7c. value constraint (string, default) 7c. Default property value. The default string is converted to the data type of the property. Note that this overrides the value constraint in the attribute to which the edge points.
7d. value constraint (string, fixed) 7d. As in 7c except that the property value cannot be changed. For example, in Java this would use the final qualifier. Note that this overrides the value constraint in the attribute to which the edge points.

4.6 Mapping attribute groups

Attribute groups may be treated two different ways.

Strictly speaking, they must be treated as a macro-like replacement mechanism used to add attribute uses to complex types. This is because no property in the schema data model -- in particular, no property in the complex type component -- is an attribute group. In this case, attribute groups are "handled" by the mapping of attribute uses in complex types. (Note that attribute group definitions could probably be mapped to macros in languages that support them, such as #define in C++.)

More loosely, the attribute uses property of a complex type could be thought of as pointing to one or more attribute groups, as well as to individual attributes. In this case, attribute groups can be mapped to classes and the edges pointing them to properties.

The following table describes how attribute group nodes are mapped to classes.

Property Mapping to object-oriented structure
8a. name, target namespace 8a. Class name. The class name must be unique among global classes. (Attribute groups are only allowed at the global level.)
8b. attribute uses 8b. See section 4.5
8c. attribute wildcard 8c. See section 4.9.
8d. annotation 8d. Comment in the code.

The following table describes how edges pointing to attribute group nodes are mapped to properties.

Property Mapping to object-oriented structure
9a. name, target namespace 9a. Property name. The property name must be unique in the class to which the property belongs.
9b. attribute uses 9b. Does not apply.
9c. attribute wildcard 9c. Does not apply.
9d. annotation 9d. Comment in the code.

4.7 Mapping model groups and named model groups

The mapping does not consider model groups and named model groups separately. Instead, the properties of these components are merged and the name and target namespace properties of unnamed model groups are set to "absent".

Model groups may be treated two different ways. First, a model group may be treated as a macro-like replacement mechanism. In this case, it is removed from the graph and replaced by edges from its parent node to its child nodes. The affect of this is that child edges are mapped to properties in the class to which the parent node of the model group is mapped.

The particles associated with the newly created edges are created by combining the particle associated with the parent edge with the particles associated with each child edge. How these edges are mapped depends on both the compositor property of the model group and the newly constructed particles. Particles are combined as follows:

1) The min occurs property is the product of both min occurs properties.

2) The max occurs property is the product of both max occurs properties, where any number times unbounded is unbounded.

3) The term property is the term property of the particle associated with the child edge.

(NOTE: If a chain of model groups is treated as macros, the particle construction operations specified above and in the following table have a cumulative affect so that the nullability and cardinality of a given property depends on all its ancestor model groups in the chain. For example, suppose a chain consists of a choice followed by two sequences and the choice is mapped to separate, nullable properties. The child edges of both sequences are mapped to nullable properties since they are ultimately included in a choice.)

Property Mapping to object-oriented structure
10a. name, target namespace 10a. Does not apply.
10b. compositor (all) 10b. Each new edge is mapped to a property in the class to which the parent of the model group is mapped.
10c. compositor (choice) 10c(i). Each new edge is mapped to a property in the class to which the parent of the model group is mapped. The min occurs property of the particle associated with each edge is set to 0. (The effect of this is to make each property nullable, as well as to pass that nullability on to the children of any child model groups that are treated as macros.)

10c(ii). All new edges are mapped to a single property with a UNION type in the class to which the parent of the model group is mapped. The type is a union of the distinct types to which the child nodes are mapped.

To determine the cardinality and nullability of the property, a single particle is constructed from the newly constructed particles as follows:

1) The min occurs property is the minimum of all min occurs properties.

2) The max occurs property is the maximum of all max occurs properties.

3) The term property is ignored, since it doesn't affect cardinality or nullability.

10d. compositor (sequence) 10d. Each new edge is mapped to a property in the class to which the parent of the model group is mapped.
10e. particles 10e. See section 4.8.
10f. annotation 10f. Comment in the code.

A model group may also be mapped to a class. In this case, the parent edge is mapped to a property in the class of the parent node and child edges are mapped to properties in the class of the model group. Again, the compositor property affects how the child edges are mapped.

ISSUE: It appears that mapping a model group to a class is possible only because of the non-normative, "for compatibility" rule in XML that content models must be deterministic. That is, if content models were not deterministic, software transferring data from an XML document to objects according to a certain mapping would not always be able to determine which objects/properties to transfer data to. Because I don't completely understand deterministic content models, I can't guarantee that mapping a model group to a class is possible in all cases. Feedback welcome.

NOTE: A consequence of mapping a model group to a class is that software that transfers data from XML documents to objects must know the schema of the XML document. If the model group is treated as a macro, then it is possible to transfer data based on the name of child elements alone.

The following table describes how model group nodes are mapped to classes.

Property Mapping to object-oriented structure
11a. name, target namespace 11a. Class name. The class name must be unique in the scope to which the class belongs.
11b. compositor (all) 11b. Child edges are mapped to properties in the class to which the model group is mapped.
11c. compositor (choice) 11c. Child edges are mapped to properties in the class to which the model group is mapped. The min occurs property of the particle associated with each edge is set to 0. (The effect of this is to make each property nullable, as well as to pass that nullability on to the children of any child model groups that are treated as macros.)
11d. compositor (sequence) 11d. Child edges are mapped to properties in the class to which the model group is mapped.
11e. particles 11e. See section 4.8.
11f. annotation 11f. Comment in the code.

The following table describes how edges pointing to model group nodes are mapped to properties.

Property Mapping to object-oriented structure
12a. name, target namespace 12a. Property name. The property name must be unique in the class to which the property belongs.
12b. compositor (all) 12b. Does not apply.
12c. compositor (choice) 12c. Does not apply.
12d. compositor (sequence) 12d. Does not apply.
12e. particles 12e. Does not apply.
12f. annotation 12f. Comment in the code.

4.8 Mapping particles

Particles are not mapped to object-oriented structures. Instead, the min occurs and max occurs properties determine nullability and cardinality of the property to which the associated edge is mapped.

Property Mapping to object-oriented structure
13a. min occurs (0) 13a. All properties to which edge is mapped are nullable.
13b. min occurs (>=1) 13b. If edge is mapped to a multi-valued property, at least min occurs values of this property must be non-null. If edge is mapped to multiple single-valued properties, min occurs of these are non-nullable.
13c. max occurs (0) 13c. Cannot occur due to schema constraints on particles. Included only for completeness.
13d. max occurs (1) 13d. Edge is mapped to a single-valued property.
13e. max occurs (>1) 13e(i). Edge is mapped to a multi-valued property of cardinality max occurs.
13e(ii). Edge is mapped to max occurs single-valued properties.
13f. max occurs (unbounded) 13f. Edge is mapped to a multi-valued property of unbounded cardinality
13g. term (model group) 13g. Does not apply. See section 4.7.
13h. term (wildcard) 13h. Does not apply. See section 4.9.
13i. term (element declaration) 13i. Does not apply. See section 4.3.

4.9 Mapping wildcards

Unless a wildcard is constrained to a finite number of namespaces, it must be mapped to an arbitrary type, such as Object in Java or pointer to void in C++. The following table describes how wildcard nodes are mapped to types.

Property Mapping to object-oriented structure
14a. namespace constraint (any) 14a. A type to which any type can be converted, such as an Object in Java or a pointer to void in C++.
14b. namespace constraint (not and namespace URI) 14b. Same as 14a.
14c. namespace constraint (not and absent) 14c. Same as 14a.
14d. namespace constraint (namespace URIs)

14d(i). If the wildcard is an attribute wildcard, the wildcard node is replaced by nodes for all namespace-qualified attributes (globally declared attributes) in the namespace. The edge leading to the node is replaced by edges leading to each new node. The new edges and nodes are then processed normally.

If the wildcard is an element type wildcard, the wildcard node is replaced by nodes for all namespace-qualified element types (globally declared element types) in the namespace. The edge leading to the node is replaced by edges leading to each new node. The new edges and nodes are then processed normally.

14d(ii). Same as 14a.
14e. namespace constraint (absent) 14e. Same as 14a.
14f. namespace constraint (namespace URIs and absent) 14f. Same as 14a.
14g. process contents 14g. Does not apply.
14h. annotation 14h. A comment in the code.

Except in case 13d(i) above, an edge pointing to a wildcard node maps to a property in the class of the parent node. The name of the property must be unique in the class. A parallel property may be created to hold metadata, such as the type to which an instance of the wildcard can be mapped (if known) or the namespace-qualified name of the attribute or element type of the instance. Except for case 13d(i), the properties of the wildcard node do not affect the mapping.

4.10 Mapping identity constraints

ISSUE: This section is admittedly incomplete. Comments welcome.

Identity constraints do not map directly onto any object-oriented structure that I am aware of. That is, I am not aware of any structural way to enforce a rule such as, "The partNumber member of the Part class must have a value that is unique across all Part objects." Instead, identity constraints appear to map best to a method on the class corresponding to the element type node on which they are declared.

This code would probably take the form of a checkConstraints() method that can enforce the constraints at any given point in time or a factory method that constructs objects that match the constraints. In either case, the XPath expression would be used (to the extent possible) to select a set of properties; the values of these properties would be checked for uniqueness or referential integrity. In many cases, it should be possible to automatically generate this code from an XML Schema and mapping to an object schema.

Identity constraints may also be mapped directly to the database. In this case, the XPath expression is mapped to a column or set of columns in a table and UNIQUE, PRIMARY KEY, or FOREIGN KEY constraints defined on these columns. Note that not all XPath expressions can be mapped to columns. (Identity constraints can also be mapped to triggers that run code to enforce them. This is comparable to the methods that enforce constraints at the object level and will not be discussed further.)

4.10.1 Where do constraints operate?

It is important to understand that constraints are defined and operate on the level of instances, not schemas. This is an artifact of defining constraints in terms of XPath, which operates at the instance level, and leads to non-obvious behavior, at least to people who are used to working with database constraints. For example, suppose there is a Part element type and that the Part element type has a (simple) PartNumber child. Now suppose that the content (value) of the PartNumber child should be unique for all Part elements in a document.

A person with a database background is likely to add a unique constraint to the Part element type declaration with a selector of self::node() and a field list of child::PartNumber. The intent is to say, "Among all elements of the Part type, make sure that the value of the PartNumber child is unique." The actual effect is to say, "For a single Part element, make sure that the value of its PartNumber child is unique." This is always true, so the constraint is satisfied even if another Part element has a PartNumber child with the same value.

What happened? The selector (self::node() in this case) operates relative to the current node. In this case, the current node is a single Part element, which is selected. The field (child::PartNumber) selects the PartNumber child. This is obviously unique within the context of a single Part element.

To rectify the situation, the selector must identify all Part elements in the document. This can be done in an arbitrarily large number of ways. For example, if all Part elements in the document have a single parent, the selector might be parent::node()/child::Part. Or the constraint might be moved to the declaration of the parent element type with a selector of child::Part and a field list of child::PartNumber. (Note that the list is defined relative to the selector -- that is, relative to the Part element.)

In reality, this is just as well. An object usually knows only about itself -- that is, it does not know about other objects of the same class. Thus, when constraints are mapped to code, the method to enforce a constraint across a set of objects should be in a position to know about all objects in that set. For example, it might be on an object that is the parent of all constrained objects or in a factory that constructs the constrained objects.

4.10.2 A quick review of XPath

Before proceeding, we quickly review XPath.

An XPath expresssion is a series of location steps separated by slashes (/). Each step selects a set of nodes in relation to the current node. These nodes become the current node(s) for the next step. The set of nodes selected by the expression are the nodes remaining after processing each step in order.

For example, the expression child::Part consists of a single step. This step selects the set of all Part elements that are children of the current node. The expression parent::node()/child::Part has two steps. The first step (parent::node()) selects a single node -- the parent of the current node. The second step (child::Part) selects all Part elements that are children of that node. Thus, the expression identifies all Part children of the parent of the current node. In other words, it selects all Part siblings of the current node, possibly including the current node.

A step in an XPath expression consists of three parts: an axis, a node test, and zero or more predicates. The axis specifies the direction to move in the document tree (which is roughly the same as the DOM document tree). For example, the child axis says to look at all child nodes. The parent axis says to look at the parent node. The self axis says to look at the current node. The descendant axis says to look at all descendant nodes. And so on.

A node test tests whether nodes encountered along the specified axis should be selected for the next step. For example, in child::Part, child is the axis and Part is the node test. A child node satisfies the node test if it is an element with the name of Part. There are node tests that check the element, attribute, and namespace name, as well as tests that check if the node is a text, comment, or processing instruction node.

A predicate is an expression that filters nodes selected by the node test. It takes the form of an equality (=, !=, >, <, >=, <=) expression. For example, [self::text()="123"] tests if the text of the current node is "123". And [child::PartNumber/self::text()="123"] tests if the current node has a PartNumber child with text of "123". Thus, the XPath expression child::PartNumber[self::text()="123"] selects all PartNumber children of the current node with text "123". And (more informally) child::Part[child::PartNumber/self::text()="123"] selects all parts that are children of the current node and have a part number of "123".

The predicate grammar is quite rich and we will not review it further.

4.10.3 Mapping XPath expressions to object code

Our next problem is to map XPath expressions to object code. Many XPath expressions map to procedural code. For example, child::PartNumber[self::text()="123"] is equivalent to:

   if (this.partNumber == "123") then ... // Process the property

(In the case of identity constraints, "processing" a property means adding it to a list of nodes to be checked for uniqueness or checking that its value corresponds to the value of a property elsewhere in the object graph.)

Identity constraints support a subset of XPath expressions. The supported axes are self, descendant-or-self, child, and attribute. The supported node tests are node(), QName (element and attribute name), and NCName:* (namespace name). Predicates are not supported.

Most of the supported XPath expressions map to sets of structurally identical objects or properties. For example, child::PartNumber is equivalent to:

   this.partNumber

where "this" might range across all the Part objects in an object graph. Such expressions are useful because they correspond to rows and columns in the database and can be used to map XML Schema identity constraints to database constraints.

The remainder of this section will discuss the subset of XPath allowed in identity constraints. During these discussions, it is important to remember that XPath operates on the instance level, while our mapping from XML Schemas to object schemas operate on the schema level. Thus, a certain number of mismatches occur.

4.10.3.1 Mapping the current node

Our first task is to map the current node -- that is, the node on which the XPath expression operates. This corresponds to the node in the graph on which the identity constraint is defined. Throughout the rest of this section, we will assume that this is a complex element type node. Thus, at the instance level, this corresponds to an object of the class to which the element type node is mapped. We can write this as:

   this
4.10.3.2 Mapping the axis

Our next task is to map the axis. The child axis corresponds to an edge leading from the current node to an element type node. (The child axis is actually a superset of these edges, as it can also point to non-element nodes, such as text, comment, and processing instruction. Such nodes do not occur in the schema graph, since it is defined at the metadata level. They also do not have analogs in the object graph corresponding to an instance document, since the object graph only models the data in the document.)

The attribute axis corresponds to an edge leading from the current node. Both the child and attribute axes map to the member-of operator. We can write this as:

   this.

The self axis leaves us at the current node. We can write this as:

   this

We cannot map the descendant-or-self axis, as it leads to a mixture of objects from different classes.

4.10.3.3 Mapping the node test

We can map two of the three supported node tests. The first is the QName test. This allows us to specify the namespace-qualified name of an element type or attribute and is useful in conjunction with the child and attribute axes. These axes identify a set of edges leading from the current node, and the QName test allows us to narrow this set to a single edge, which leads to the specified element type or attribute node. For example, if this edge leads to the PartNumber element type and is mapped to the partNumber property, we can write this as:

   this.partNumber

The second test we can map is the node() test. When combined with the self axis, we get the same object back. That is, self::node() can be written as:

   this

(When combined with the child or attribute axes, the node() operator has a different effect. It enumerates all children or attributes and leads to a mixture of objects from different classes or a mixture of different properties.)

We cannot map the namespace (NCName:*) test because it leads to a mixture of different properties.

4.10.3.4 Mapping XPath expressions at the schema level

The subset of XPath discussed above can be mapped at the schema level. That is, it can be expressed purely in terms of classes and properties. This subset is useful (a) because it can be used to easily generate code to enforce identity constraints in the object graph, and (b) because it can be used to map identity constraints to database constraints.

To summarize, the subset is as follows:

4.10.4 Mapping identity constraints to relational databases

Technically, identity constraints in an XML Schema cannot be safely mapped to identity constraints in a relational database. The reason for this is that the constraints only ensure uniqueness within a fragment of a single document, while database constraints require uniqueness across all rows in a table. The latter is roughly equivalent to uniqueness across all documents that match a given schema.

In reality, it is reasonable to assume that many identity constraints will meet the requirement of uniqueness across all documents. However, XML Schema identity constraints can be mapped to relational constraints only for a subset of all possible XPath expressions. For more information, see section 4.10.5.

4.10.5 Mapping identity constraint components

The following table shows how identity constraint components are used when mapping identity constraints to object code:

Property Mapping to object-oriented structure
15a. name, target namespace 15a. Name of method enforcing the constraint.
15b. identity-constraint category (key)

15b. Code. Calls the code in 15e to get a list of objects and/or properties according to the selector XPath expression. For each object or property in this list, calls the code in 15f to get a property (or set of properties) according to the fields XPath expression.

The method then checks that (a) there is a non-null property (or set of properties) in the second list for each member of the first list, and (b) the value of each property (or set of properties) in the second list is unique within that list.

15c. identity-constraint category (keyref) 15c. Code. Creates a list of properties (or sets of properties) as in 15b, then creates a second list (using a different base object) using the code in 15g. Finally, it compares the two lists. For each property (or set of properties) in the first list, there must be an entry in the second list that is pairwise equal.
15d. identity-constraint category (unique) 15d. As for 15b, except that the code allows null properties (or sets of properties) in the second list.
15e. selector 15e. Code. Returns a list of objects and/or properties relative to the current object according to the selector XPath expression.
15f. fields 15f. Code. For a given object or property, returns a property (or set of properties) according to the fields XPath expression.
15g. referenced key 15g. Similar to 15e and f. The selector and fields XPath expressions are different but the current object is the same.
15h. annotation 15h. A comment in the code.

The following table shows how identity constraint components are used when mapping identity constraints to database constraints:

Property Mapping to object-oriented structure
16a. name, target namespace 16a. Does not apply.
16b. identity-constraint category (key) 16b. The constraint is mapped to a PRIMARY KEY constraint.
16c. identity-constraint category (keyref) 16c. The constraint is mapped to a FOREIGN KEY constraint.
16d. identity-constraint category (unique) 16d. The constraint is mapped to a UNIQUE constraint.
16e. selector

16e. Must identify a single table. That is, starting from the element type node on which the identity constraint is defined, the XPath expression must lead to a complex element type node. The table to which this node is ultimately mapped is the table to which the constraint applies.

The steps used in the XPath expression must resolve to child::QName, self::QName, self::node(), or parent::node(). If the expression cannot be resolved to a series of steps using only these constructs, the constraint cannot be mapped.

16f. fields

16f. Must identify one or more columns in the table in 16e. That is, starting from the node identified in 16e, each XPath expression must lead to an edge leading from the node in 16e to simple element type node or an attribute node. These are the columns in the constraint.

The steps used in the XPath expression must resolve to child::QName or attribute::QName. If the expression cannot be resolved to a series of steps using only these constructs, the constraint cannot be mapped.

16g. referenced key

16g. The selector and fields properties of the referenced key identity constraint are used to determine the table and key columns referenced in a FOREIGN KEY constraint.

These properties are processed and the same restrictions apply to them as are specified in 16e and 16f.

16h. annotation 16h. Does not apply.

4.11 Mapping substitution groups

Substitution groups appear to be representable as choice groups. For example, assume that a content model contains a element type that heads a substitution group. An equivalent content model -- that is, a content model that schema-validates the same documents -- can be constructed by replacing the element type with a choice group constructed from the substitution group.

(Of course, the obvious difference between substitution groups and choice groups is that substitution groups can be used to extend schemas in a way that choice groups cannot. For example, suppose I want to create a schema from an existing schema. One way I might do this is to add a new element type and declare it as part of the substitution group of an element type in the existing schema. I can do this without modifying the existing schema; if I use choice groups, I would have to modify the existing schema. However, the choice group does appear to represent an accurate picture of a schema at any given point in time.)

The choice group is constructed as follows:

  1. Add all element types whose substitution group affiliation property is the specified element type.
  2. If the prohibited substitutions property of the complex type in whose content model the element type appears is extension, remove any element types whose type is derived from the type of the specified element type by extension.
  3. Perform the same operation as (2), except for restriction.

Thus, substitution groups can be handled by pre-processing the schema graph. That is, for each edge pointing to an element type that heads a substitution group, construct a choice group, add the choice group to the graph (including edges leading from the choice group to the relevant element type declaration nodes), and replace the original edge with an edge pointing to the choice group.

(As far as I can tell, the substitution group exclusions property of the element type that heads the group is not relevant, as it prevents certain element types from being part of the substitution group in the first place. I believe that the substitutions value of the disallowed substitutions property performs a similar function. That is, it prohibits a substitution group with the specified element type as its head from being constructed at all. The extension and restriction values of this property are too horrible to contemplate, as they appear to open the possibility -- actually opened by xsi:type -- that unknown types will appear at runtime, sort of like previously unknown relatives dropping by and telling you that they are happy to finally meet you and will be moving in immediately. This effectively turns every element type into an any wildcard and results in a useless mapping.)

ISSUE: Have I interpreted the various substitution group properties correctly -- especially substitution group exclusions, disallowed substitutions, and prohibitied substitutions? In spite of having read the spec numerous times, I still am not 100% clear on these.

ISSUE: Is there another way to interpret substitution groups and their various properties? I have a vague feeling that they map to inheritance or finality of properties or something, but am not sure exactly what this is.

4.12 Mapping order

The order of child nodes in a model group (sibling order) is significant in XML. Whether it is actually significant in practice depends on the application. In general, it is significant to document-centric applications and to data-centric applications that want to validate (or schema-validate) documents before processing them and not significant to data-centric applications that do not want to validate documents before processing them.

(Data-centric applications generally should validate any documents they receive from untrusted sources, although how often this occurs in practice is unknown. For validation (schema-validation) to succeed, sibling elements must occur in the correct order, which means that order must be mapped. The only exception to this is when all groups can be used. Unfortunately, these are inadequate for modeling many data-centric documents, which can have multiply-occurring siblings of the same type. An example of this is a sales order document that has header elements (date, customer, etc.) as siblings of multiple line item elements.)

There are two ways to map sibling order:

The order of values in a multi-valued attribute is also significant in XML. Attribute order must be mapped to an order property, each of which has its own order space. The order of attributes in an element is not significant in XML and there is therefore no need to map the order of these.

4.13 Mapping notations

This paper does not consider how to map notations. Most reasonably, these map to (possibly complex) data types.

4.14 Mapping simple types

Simple types map to scalar data types. The following table describes how simple types are mapped to scalar data types.

Property Mapping to object-oriented structure
17a. name, target namespace 17a. Data type name. The name is not required to be unique. That is, multiple simple types can be mapped to the same data type. However, their facets are likely to be mapped differently.
17b. base type definition 17b. This is followed back through the type definition hierarchy until a type is reached that can be mapped directly to a data type in the object-oriented language. This is the type to which the simple type is mapped.
17c. facets, fundamental facets 17c. Help determine the data type to which the simple type is mapped. Any facets not used in this mapping must be mapped to code in a mutator used to set a property that uses the type.
17d. final 17d. Determines whether the data type is final. Depending on the language used, it may or may not be possible to map this.
17e. variety (atomic) 17e. A single-valued type.
17f. variety (list) 17f. A multi-valued type.
17g. variety (union) 17g. A union.
17h. annotation 17h. A comment in the code.

5.0 Mapping object schemas to relational database schemas

This section discusses how to map object schemas to relational database schemas. It is less than complete, covering only enough material to map XML Schemas to relational schemas by way of object schemas.

Not discussed are non-normal mappings, such as when parent and child classes are mapped to the same table, as well as mappings that map metadata to data, such as when all classes are mapped to a single table with columns for class name, property name, and property value.

Object-oriented Structure Mapping to relational database structure
18a. Abstract class 18a. Abstract classes are not mapped except when mapping inheritance. See 18c.
18b. Class

18b. Table. This is known as a class table.

An object is represented by a row in a class table.

18c. Inheritance

18c(i) The superclass and subclass are mapped to separate tables with a unique key / foreign key joining them. The unique key is in the superclass table.

An object is represented by a row in each table.

18c(ii) The superclass properties are stored in the subclass table.

An object is represented by a row in this table.

18d. Single-valued property with scalar data type

18d(i). Column in class table. This is known as a property column. The data type determines the set of possible data types of the column. (A data type of pointer to void or Object is mapped to BLOB.)

A property is represented by a value in a property column.

18d(ii). Property column in separate table. The table is known as a property table. The property table is joined to the class table with a unique key / foreign key relationship and the unique key is in the class table.
18d(iii). Same as 18d(ii), except that the unique key is in the property table. An example of this is a lookup table.
18d(iv). Same as 18d(ii) except that the table has columns for more than one property in the same class. The properties must all be single-valued.
18e. Multi-valued (collection) property with scalar data type 18e(i). Multiple property columns in class table. Each position in the collection is mapped to a specific property column. This is possible only if the collection has a known, finite maximum number of values.
18e(ii). Property column in a property table. There is one row for each value in the collection. The property table is joined to the class table with a unique key / foreign key relationship and the unique key is in the class table.
18e(iii). Same as 18e(ii) except that the unique key is in the property table. This is possible only if the collection has a known, finite maximum number of values. In this case, there is one foreign key column in the class table for each possible row in the property table. An example of this is a lookup table, where multiple lookups of the same type are needed, as when a customer specifies their top three choices of car colors.
18e(iv). Same as 18e(ii) except that the property table has columns for more than one property in the same class. If the cardinality of the properties are different, missing values are represented by NULLs.
18f. Single-valued property with union data type 18f(i). Set of nullable property columns in the class table, one for each data type in the union. A given property value results in one non-NULL column value.
18f(ii). Multiple property tables, each with one column. There is one table for each data type in the union and the unique key is in the class table. A given property value results in one row in one table.
18g. Multi-valued (collection) property with union data type 18g(i). Multiple property tables, each with one column. There is one table for each data type in the union and the unique key is in the class table. A given multi-valued property results in multiple rows distributed across one or more of these tables.
18g(ii). Mappings similar to 18e(i), (iii) and (iv) are also possible, but with the possible exception of 18e(iv) -- where multiple multi-valued union properties are mapped to a set of property tables -- none of these seems to be of any use.
18h. Single-valued property with class data type The class containing the property is called the parent class and the class corresponding to the property is called the child class.
18h(i). Unique key / foreign key relationship between the tables of the two classes. The unique key is in the table of the parent class.
18h(ii). Unique key / foreign key relationship between the tables of the two classes. The unique key is in the table of the child class.
18i. Multi-valued property with class data type 18i(i). Unique key / foreign key relationship between the tables of the two classes. The unique key is in the table of the parent class. There is one row in the table of the child class for each property value.
18i(ii). Same as 18i(i) except that the unique key is in the table of the child class. This is possible only if there is a known, finite maximum number of values in the collection. In this case, there is one foreign key column in the table of the parent class for each possible row in the property table.
18j. Property nullability 18j. Column nullability.
18k. Property finality 18k. Column writeability. That is, a final property maps to a read-only column and a non-final property maps to a read-write column.
18l. Property default 18l. Column default. The property default is converted to the data type of the column.
18m. Identity constraints 18m. See sections 4.10.4 and 4.10.5.
18n. Comments in code 18n. Does not apply.