Extreme Markup Languages

Data Binding Using W3C XML Schema Annotations

K. Ari Krupnikov
University of Edinburgh
Language Technology Group

Henry S. Thompson
University of Edinburgh
Language Technology Group

Mapping XML Schemas to class definitions and relational schemas allows for seamless marshaling and unmarshaling of an object's state to and from XML documents. W3C XML Schemas provide a rich set of datatypes and validity rules, both built-in, and user definable. It is possible to map data structures defined in a Schema document to data structures in object oriented languages and relational databases. This paper presents approaches to automating such mapping.

In simple cases, a Schema document can be generated based on a class definition, or a set of table definitions based on a Schema document with generic heuristics based on XML normal forms governing this generation process. In more interesting cases, a mapping between existing classes and Schemas is needed, such as when an existing class is required to marshal its state to an XML document that is valid with respect to a standard Schema.

A mechanism built into the Schema Recommendation is schema annotations. Annotations can be used to modify generic heuristics of mapping schemas to classes, or replace them completely with custom mappings. Schema annotations are fragments of XML text embedded in a Schema document that can be interpreted by the mapping software.

There are numerous schema languages being developed and proposed for XML. In this paper, we discuss W3C XML Schema; for brevity, we refer to it simply as "schema".

1 XML Instance Documents and In-Meory Representation

It is possible to represent an object's state as an XML document to serialize it. Such serialization would map an object's data members to nodes in a document. The structure of mapped objects and documents need not be identical. Indeed, some constructs in programming languages, such as the distinction between arrays and linked lists, do not map trivially to XML; the converse is also true.

In this paper, we use a somewhat artificial example, taken from Schema Part 0: Primer, to demonstrate how minimal human input may produce automated, meaningful mappings.

The document is reproduced here unchanged :

<?xml version="1.0"?>
<purchaseOrder orderDate="1999-10-20">
 <shipTo country="US">
  <name>Alice Smith</name>
  <street>123 Maple Street</street>
  <city>Mill Valley</city>
 <billTo country="US">
  <name>Robert Smith</name>
  <street>8 Oak Avenue</street>
  <city>Old Town</city>
 <comment>Hurry, my lawn is going wild!</comment>
  <item partNum="872-AA">
   <comment>Confirm this is electric</comment>
  <item partNum="926-AA">
   <productName>Baby Monitor</productName>

The schema we use is based on the one in the Primer. We don't include it here because of space constraints, but include fragments that are augmented with mapping information.

2 Meaningful Defaults

The Schema recommendation is divided into two parts - structures and datatypes. Defaults for mapping, too, can be divided into two - normal forms for structures and mappings for builtin datatypes.

2.1 XML Normal Forms as a Basis for Mapping Structure

In an accompanying paper,[Thompson (2001)], Henry Thompson introduces the notion of "XML normal forms". Inspired, in part, by relational normal forms, [Codd (1970)], these conventions for XML representation of structured data can be a basis for meaningful defaults in mapping XML schemas to object-oriented classes, relational tables or other data structures. Unlike relational normal forms, XML normal forms do not represent progressive abstractions, but different, unrelated ones.

A number of normal forms have been proposed; some are discussed in the paper mentioned above. In this paper, we use a simple form in which both elements and attributes in a document are assumed to represent properties, and their names are assumed to represent the properties' names; simple types are assumed to represent atoms and complex types, relations. Other normal forms lend themselves equally well to mapping techniques described in this paper, through application of slightly different heuristics.

A normal form may be formally defined in an XML document. Here is the definition for the normal form described above:




2.2 Default Mappings for Builtin Datatypes

Schema Part 2: Datatypes describes a wealth of builtin datatypes. We use a simple XML format to encode a mapping from these builtin types to types available in host languages. Here is a fragment from type-defaults.java.xml, the mapping for Java:

  <map:simpleType name="string">
    <map:ping primitive="false">java.lang.String</map:ping>
  <map:simpleType name="boolean">
    <map:ping map:lang="Java" primitive="true">boolean</map:ping>
  <map:simpleType name="float">
    <map:ping map:lang="Java" primitive="true">float</map:ping>
  <!-- other builtin types -->

These defaults may be overridden for specific components in a schema as described below, or gobally, by creating an alternative mapping document.

2.3 The map namespace

We use the map namespace to describe mappings; its URI is http://www.cogsci.ed.ac.uk/~kari/schema-mapping. It is used to describe both defaults, as in this case, and to override defaults as we describe in the next section.

3 Modifying the Normal Form

In the previous section, we described defaults of a particular normal form and host language. In many cases it may be desirable to deviate from these defaults. There may be inconsistencies in naming conventions between XML and the host language; certain syntactic constructs in the host language may require extra levels of enclosing elements for clarity.

Note that in the extreme case, every default may be overridden and the normal form ignored.

There are two ways to include information in a schema document - appinfo children of annotation elements and attributes in namesaces other than the schema namespace. For our purposes, the difference is negligible; we use the attributes.

Two examples we show here are overriding type name default and promoting removing an element, promoting its children to the next level of hierarchy. Here is fragment of the schema document:

 <xs:complexType name="PurchaseOrderType"
         mixed="false" map:name="PurchaseOrder">
   <xs:element name="shipTo" type="Address"/>
   <xs:element name="billTo" type="Address"/>
   <xs:element ref="comment" minOccurs="0"/>
   <xs:element name="items" type="Items" map:to="promote"/>
  <xs:attribute name="orderDate" type="xs:date"/>

3.1 Overriding type name default

As an example we show how an element can be mapped to a class with a different name. The XML document's top-level element is purchaseOrder. In Java, class names start with a capital letter by convention. In the schema document, the xs:complexType element has a map:name="PurchaseOrder" attribute.

3.2 Changing the hierarchy

In the sample XML document, item elements are grouped within an items element. This grouping is redundant for an in-memory representation as it can be better expressed by an array. To remove the items element and promote its children to the next higher level of hierarchy, the xs:element element has a map:to="promote" attribute.

4 Reflected PSVI as a Source of Mapping Information

XML documents that are valid wrt to a schema contain implicit information that is found in the schema rather than the document itself . Such documents' InfoSet, known as PSVI [Post Schema Validation InfoSet] combines information from a document instance and the schema associated with the it. In PSVI, every datum from the instance document is associated with type information from the schema definition. Based on defaults dictated by an XML normal form, this information can be used to construct an in-memory representation of the information contained in an instance document.

PSVI may be available as an object in memory that may be queried and based on which an in-memory representation of the document data may be constructed; it may be available as a stream of events in response to which the in-memory representation may be built incrementally. PSVI may also be serialized as an XML document, known as reflected PSVI. In this paper, we concentrate on the reflected PSVI. Naturally, techniques described here are as applicable to other PSVI representations as they are to reflected PSVI.

We use XSLT stylesheets to gather information from the PSVI. The result of these transformations is not a binary data structure but source code in the host language that can build such structures.1 1 An intriguing alternative would be to use XSLT's extension function mechanism to construct native objects in the host environment.

5 Decorated Pseudo-Instance as an Intermediate Step

The form in which the PSVI is available to an application is non-standardized and no-portable; the generated text or binary representation is naturally specific to the target environment. The process of building a run-time representation therefore can be logically separated into two phases: gathering relevant information from the PSVI building some sort of intermediate representation that is generic enough to be useful in a variety of environments, and building the final representation in whatever format is required by the host environment.

In this step we build what we call a decorated pseudo-instance. All information pertinent to mapping that was gathered from the PSVI is added to the instance document in the form of attributes in the map namespace added to every element. Otherwise, the document is unmodified; elements are not removed, renamed or moved.

For example the items element now looks like this:

<items map:item-to="promote" map:item-name="items"
		  map:minOccurs="1" map:maxOccurs="1" map:type-to="promote"

The decorated instance contains, for every node, a complete set of instructions needed to create a runtime in-memory representation: name of host language type (map:item-name), whether this is a primitive or reference type an whether it should be present in in-memory representation (map:item-to), whether this is a scalar or array member (map:maxOccurs).

6 Output

Based on the intermediate representation described in the previous section, we can build the final representation in any specific environment. Here, we present to examples: code that initializes a Java object and FOPL [Firs Order Predicate Logic] assertions. Both examples use gensyms [generated symbols] to denote individual objects. These gensyms have no special significance other than their uniqueness within the scope of a document or object; in this implementation they are imply the hexadecimal sequential numbers of corresponding nodes in the source document.

6.1 FOPL Assertions

Here, we present a list of assertions that can be deduced from the source document. Individuals are printed in bold; named binary relations between individuals and other individuals or are represented by square brackets. For each atom, its type is indicated.

Note that the type of the top-level individual is not purchaseOrder as the name of the element in the document is, but PurchaseOrder as is mandated by the mapping. Also, note that the items element is gone, its children promoted to the next higher level, as specified by the mapping.

PurchaseOrder (N2)

  Address (NF)
    shipTo [N2, NF]
    name [NF, string ("Alice Smith")]
    street [NF, string ("123 Maple Street")]
    city [NF, string ("Mill Valley")]
    state [NF, string ("CA")]
    zip [NF, decimal ("90952")]

  Address (N3F)
    billTo [N2, N3F]
    name [N3F, string ("Robert Smith")]
    street [N3F, string ("8 Oak Avenue")]
    city [N3F, string ("Old Town")]
    state [N3F, string ("PA")]
    zip [N3F, decimal ("95819")]
    comment [N2, string ("Hurry, my lawn is going wild!")]

  Item (N7E)
    item [N77, N7E]
    productName [N7E, string ("Lawnmower")]
    quantity [N7E, positiveInteger ("1")]
    price [N7E, decimal ("148.95")]
    comment [N7E, string ("Confirm this is electric")]

  Item (NA6)
    item [N77, NA6]
    productName [NA6, string ("Baby Monitor")]
    quantity [NA6, positiveInteger ("1")]
    price [NA6, decimal ("39.98")]
    shipDate [NA6, date ("1999-05-21")]

6.2 Java

Here, we present code that can initialize a Java object based on an XML document. We present the source code, but a run-time system can as easily execute the commands instead of generating them.

This code assume that all user-defined classes have zero-argument constructors and public access to members. Note that atoms' types are mapped to specific Java classes per Java mapping discussed in the section on defaults, e.g. price's xsd:decimal is mapped to java.math.BigDecimal. Also note that our example has no types that, by default, map to primitive Java types. If there were any, those atoms would naturally have been initialized as primitive variables in Java, through literal assignment as opposed to new object construction syntax.

public static PurchaseOrder init () {
  PurchaseOrder N2 = new PurchaseOrder ();
    Address NF = new Address ();
    N2.shipTo = NF;
      java.lang.String N17 = new java.lang.String ("Alice Smith");
      NF.name = N17;
      java.lang.String N1F = new java.lang.String ("123 Maple Street");
      NF.street = N1F;
      java.lang.String N27 = new java.lang.String ("Mill Valley");
      NF.city = N27;
      java.lang.String N2F = new java.lang.String ("CA");
      NF.state = N2F;
      java.math.BigDecimal N37 = new java.math.BigDecimal ("90952");
      NF.zip = N37;
    Address N3F = new Address ();
    N2.billTo = N3F;
      java.lang.String N47 = new java.lang.String ("Robert Smith");
      N3F.name = N47;
      java.lang.String N4F = new java.lang.String ("8 Oak Avenue");
      N3F.street = N4F;
      java.lang.String N57 = new java.lang.String ("Old Town");
      N3F.city = N57;
      java.lang.String N5F = new java.lang.String ("PA");
      N3F.state = N5F;
      java.math.BigDecimal N67 = new java.math.BigDecimal ("95819");
      N3F.zip = N67;
    java.lang.String N6F = new java.lang.String
	("Hurry, my lawn is going wild!");
    N2.comment = N6F;
      Item[] aN7E = new Item[2];  // initialize an array
      N2.item = aN7E;  // assign the array to it's parent's member
      Item N7E = new Item ();
      N2.item[0] = N7E;
        java.lang.String N86 = new java.lang.String ("Lawnmower");
        N7E.productName = N86;
        java.math.BigInteger N8E = new java.math.BigInteger ("1");
        N7E.quantity = N8E;
        java.math.BigDecimal N96 = new java.math.BigDecimal ("148.95");
        N7E.price = N96;
        java.lang.String N9E = new java.lang.String
	    ("Confirm this is electric");
        N7E.comment = N9E;
      Item NA6 = new Item ("");
      N2.item[1] = NA6;
        java.lang.String NAE = new java.lang.String ("Baby Monitor");
        NA6.productName = NAE;
        java.math.BigInteger NB6 = new java.math.BigInteger ("1");
        NA6.quantity = NB6;
        java.math.BigDecimal NBE = new java.math.BigDecimal ("39.98");
        NA6.price = NBE;
        java.util.Date NC6 = new java.util.Date ("1999-05-21");
        NA6.shipDate = NC6;
  return N2;



For interpreted languages, the difference is immaterial.


[Codd (1970)] Codd, E. 1970 "A Relational Model for Large Shared Data Banks," CACM, June 1970

[Thompson (2001)] Thompson, H. 2001 "Normal Form Conventions for XML Representations of Structured Data", to appear