!!Why do we need flat schemas?

If we look at the structure of our first schema, we see that it follows the structure of the instance document. The effort involved in writing this first schema has been pretty much limited to inserting "element", "attribute" and "text" elements in the schema each time we've seen an element, attribute or text node in the instance document! This flavor of our schema can almost be seen as some kind of XML serialization of the XML infoset (i.e. of the information available in the document) and could be easily automated. Actually, it has been automated and this is the principle behind Examplotron which will be described in "Chapter 14: Generating Relax NG schemas".

However, there are a couple of downsides for modeling documents with this style of "Russian doll" schemas: first they are not modular and get difficult to read and maintain when documents are complex and second they cannot represent recursive models.

The lack of modularity can be seen even for a document as simple as our first example: we have a "name" element which is used with the same model both within the "character" and the "author" elements. In our first schema we need to give the definition of what a "name" is in both contexts:

[http://localhost/rngbook/2names.png]

One might think that this is not a big deal, but that's not completely true. The additional verbosity here is low because the definition of the "name" element is simple, but the principle would have been the same if the definition had been longer. As with any redundancy this makes the maintenance of the schema more error prone: if I need to update the definition of the name element, I need to update it twice. The rules of common sense used in any programming language do apply to XML schema languages as well!

An other rule which can be borrowed from programming languages is about modeling recursive models, i.e. models such as XHTML in which the "div" elements may be embedded within other "div" elements without any restriction in the number of levels. To be able to model such recursive models, it is clear that I can't just copy the definition of the "div" element again and again and that I need a way to define and reference the content model of the "div" element recursively. In the course of this chapter we will give different examples covering both modularity and recursive models.

!!Defining named patterns

For Relax NG, the answer to these issues is of course through patterns (haven't we learned that Relax NG is about patterns and patterns only?) which can hold a name and be referenced through this name.

In the XML syntax, the definition of named patterns is done through "define" elements. To define a named pattern containing our "name" element we would thus write:

 <define name="name-element">
  <element name="name">
   <text/>
  </element>
 </define>

The compact syntax uses a construction similar to a programming language format and the same definition would be written as:

 name-element = element name {text}

We are not limited to embedding a single element (or attribute definition in a named pattern and we could note that a common group composed of an "id" attribute, a "name" element and an optional "born" element are present in the same order and with the same definition in both the "author" and the "character element":

[http://localhost/rngbook/named2.png]

 <define name="common-content">
  <attribute name="id"/>
  <element name="name">
   <text/>
  </element>
  <optional>
   <element name="born">
    <text/>
   </element>
   </optional>
 </define>

or:

 common-content =
   attribute id { text },
   element name { text },
   element born { text }?


!!Referencing named patterns

Defining a named pattern has been easy but referencing it is still simpler!

Using the XML syntax, references are done using a "ref" element, for instance to define the "author" element using a reference to "name-element":

      <element name="author">
       <attribute name="id"/>
       <ref name="name-element"/>
       <optional>
        <element name="born">
         <text/>
        </element>
       </optional>
       <optional>
        <element name="died">
         <text/>
        </element>
       </optional>
      </element>

To reference a named pattern in the compact syntax, you just name it:

    element author {
      attribute id { text },
      name-element,
      element born { text }?,
      element died { text }?
    }

The same would be applied to referencing the "common-content" named pattern:

      <element name="author">
       <ref name="common-content"/>
       <optional>
        <element name="died">
         <text/>
        </element>
       </optional>
      </element>

Or:

    element author {
      common-content,
      element died { text }?
    }


!!Grammar and start elements

In our first example, the Russian doll style that we had adopted was such that the definition of the root element (i.e. in our case the library element) could be used as a container for the whole schema. When we define named patterns, Relax NG requires that we define them globally. Thus we will need a container to contain at least the definition of the root element of the instance document and the definitions of the named patterns. This container is what Relax NG calls a "grammar" and it uses, of course, a "grammar" element. When we use a grammar element, Relax NG requires that we declare explicitly which will be the root element (or elements since we will see later on how to define choices between patterns) and this is done using a "start" element. The top level structure of the schema defining a pattern "name-element" would thus be:


 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
  <start>
   <element name="library">
    .../...
   </element>
  </start>
  <define name="name-element">
   .../...
  </define>
 </grammar>


Or, using the compact syntax:

 grammar {
  name-element = .../...
  start =
   element library {
   .../...
  }
 }

!!All together




We have seen the different bits and pieces needed to define and reference patterns and it's time to put them all together and see a complete schema using them. The first exercise we can do is to define a "DTD like" Relax NG schema which defines each of the elements in its own named pattern.

The full schema could be:

<?xml version="1.0" encoding="UTF-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">

  <start>
   <ref name="element-library"/>
  </start>

  <define name="element-library">
   <element name="library">
    <oneOrMore>
     <ref name="element-book"/>
    </oneOrMore>
   </element>
  </define>

  <define name="element-book">
   <element name="book">
    <attribute name="id"/>
    <attribute name="available"/>
    <ref name="element-isbn"/>
    <ref name="element-title"/>
    <oneOrMore>
     <ref name="element-author"/>
    </oneOrMore>
    <zeroOrMore>
     <ref name="element-character"/>
    </zeroOrMore>
   </element>
  </define>

  <define name="element-isbn">
   <element name="isbn">
    <text/>
   </element>
  </define>

  <define name="element-title">
   <element name="title">
    <attribute name="xml:lang"/>
    <text/>
   </element>
  </define>

  <define name="element-author">
   <element name="author">
    <attribute name="id"/>
    <ref name="element-name"/>
    <optional>
     <ref name="element-born"/>
    </optional>
    <optional>
     <ref name="element-died"/>
    </optional>
   </element>
  </define>

  <define name="element-name">
   <element name="name">
    <text/>
   </element>
  </define>

  <define name="element-born">
   <element name="born">
    <text/>
   </element>
  </define>

  <define name="element-died">
   <element name="died">
    <text/>
   </element>
  </define>

  <define name="element-character">
   <element name="character">
    <attribute name="id"/>
    <ref name="element-name"/>
    <optional>
     <ref name="element-born"/>
    </optional>
    <ref name="element-qualification"/>
   </element>
  </define>

  <define name="element-qualification">
   <element name="qualification">
    <text/>
   </element>
  </define>

 </grammar>

Or:

 grammar{

 start = element-library

 element-library = element library {element-book +}

 element-book = element book {
     attribute id { text },
     attribute available { text },
     element-isbn,
     element-title,
     element-author+,
     element-character*
    }

 element-isbn = element isbn { text }

 element-title = element title {
     attribute xml:lang { text },
     text
    }

 element-author = element author {
     attribute id { text },
     element-name,
     element-born?,
     element-died?
    }

 element-name = element name { text }

 element-born = element born { text }

 element-died = element died { text }

 element-character = element character {
     attribute id { text },
     element-name,
     element-born?,
     element-qualification
    }

 element-qualification = element qualification { text }

 }


This "DTD style" is pretty common and has the advantage to facilitate finding the definition of each element in the schema. Another popular style is to define the content of each element as a pattern:

 <?xml version="1.0" encoding="UTF-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">

  <start>
   <element name="library">
    <ref name="library-content"/>
   </element>
  </start>

  <define name="library-content">
   <oneOrMore>
    <element name="book">
     <ref name="book-content"/>
    </element>
   </oneOrMore>
  </define>

  <define name="book-content">
   <attribute name="id"/>
   <attribute name="available"/>
   <element name="isbn">
    <ref name="isbn-content"/>
   </element>
   <element name="title">
    <ref name="title-content"/>
   </element>
   <oneOrMore>
    <element name="author">
     <ref name="author-content"/>
    </element>
   </oneOrMore>
   <zeroOrMore>
    <element name="character">
     <ref name="character-content"/>
    </element>
   </zeroOrMore>
  </define>

  <define name="isbn-content">
   <text/>
  </define>

  <define name="name-content">
   <text/>
  </define>

  <define name="born-content">
   <text/>
  </define>

  <define name="died-content">
   <text/>
  </define>

  <define name="qualification-content">
   <text/>
  </define>

  <define name="title-content">
   <attribute name="xml:lang"/>
   <text/>
  </define>

  <define name="author-content">
   <attribute name="id"/>
   <element name="name">
    <ref name="name-content"/>
   </element>
   <optional>
    <element name="born">
     <ref name="born-content"/>
    </element>
   </optional>
   <optional>
    <element name="died">
     <ref name="died-content"/>
    </element>
   </optional>
  </define>

  <define name="character-content">
   <attribute name="id"/>
   <element name="name">
    <ref name="name-content"/>
   </element>
   <optional>
    <element name="born">
     <ref name="born-content"/>
    </element>
   </optional>
   <element name="qualification">
    <ref name="qualification"/>
   </element>
  </define>

 </grammar>

Or:

 grammar {

 start = element library {library-content}

 library-content =
   element book { book-content } +

 book-content =
     attribute id { text },
     attribute available { text },
     element isbn { isbn-content },
     element title { title-content },
     element author { author-content }+,
     element character { character-content }*

 isbn-content = text

 name-content = text

 born-content = text

 died-content = text

 qualification-content = text

 title-content =
   attribute xml:lang { text },
   text

 author-content =
   attribute id { text },
   element name { name-content },
   element born { born-content }?,
   element died { died-content }?

 character-content =
   attribute id { text },
   element name { name-content },
   element born { born-content }?,
   element qualification { qualification }

 }

Note that we will see in "Chapter 12: Writing Extensible Schemas" that the style (Russian doll, DTD like or content oriented like this schema) has an impact on the extensibility of your schemas and the last option we've seen is the most extensible.


We could also revisit the "bizarre patterns" mentioned "Chapter 2: Simple Is Beautiful":

[http://localhost/rngbook/rng-full-pattern.png]

When we think about it, this case is not so uncommon. Let's say for instance that we have a first pattern named "book-basic" with the "id" attribute and the "isbn", "title" one or more "author" and an optional "character" element and a second pattern to extend the first one named "book-extended" with the "available" attribute and zero or more "character" elements. Well, yes that may happen if the the team in charge of defining the "book-basic" pattern has been short visioned and has thought that one character was enough for a book!

Updating the "DTD like" flavor of our schema is just a matter of splitting the definition of the "book" element:

  <define name="element-book">
   <element name="book">
    <ref name="book-basic"/>
    <ref name="book-extended"/>
   </element>
  </define>

  <define name="book-basic">
   <attribute name="id"/>
   <ref name="element-isbn"/>
   <ref name="element-title"/>
   <oneOrMore>
    <ref name="element-author"/>
   </oneOrMore>
   <optional>
    <ref name="element-character"/>
   </optional>
  </define>

  <define name="book-extended">
   <attribute name="available"/>
   <zeroOrMore>
    <ref name="element-character"/>
   </zeroOrMore>
  </define>

Or:

 element-book = element book {
     book-basic,
     book-extended
    }

 book-basic =
     attribute id { text },
     element-isbn,
     element-title,
     element-author+,
     element-character?

 book-extended =
     attribute available { text },
     element-character*


!!Non restrictions

One of the nice features of Relax NG is that some of the restrictions which add a lot of complexity in other schema languages are non-restrictions for Relax NG; we've seen at least two of them in this chapter. The first one is the ability to define attributes wherever you want in your patterns. This doesn't make a big difference when you define the content model of each elements straightforwardly like in our first schema, but this makes a huge difference when we start to combine patterns as we've done with our bizarre model. Without this non-restriction, it would have been impossible to define one attribute in the pattern "book-start" and a second one in the pattern "book-end".

The other non-restriction found in the chapter is the fact that Relax NG pays no attention to the pattern used to match a node of the instance document when there is several possibilities. Again, in our bizarre pattern, if we have a document with a book having only one author, there is no way to tell if this author matches the optional "author" element of the pattern "book-start" or the zero or more "author" elements of the pattern "book-author". This would be considered as an ambiguity intolerable for other schema languages. In this case, Relax NG considers that even though there is an ambiguity, since there is at least one interpretation of the schema for which the document is valid then the document should be considered as valid. We will learn more about these ambiguities and their consequences on the uses which can be done of the schemas in "Chapter 16: Determinism and Datatype Assignment".


!!Recursive models

As mentioned in the introduction of this chapter, named patterns are the only way to represent recursive models. We haven't seen yet all the building blocks needed to define a XHTML "div" element, but we can take a simpler example. If our library is divided into categories, each of them having a title, zero or more embedded categories and zero or more books, we could write (assuming that named patterns have been defined for the "book" element:

 <define name="category">
  <element name="category">
   <element name="title">
    <text/>
   </element>
   <zeroOrMore>
    <ref name="category"/>
   </zeroOrMore>
   <zeroOrMore>
    <ref name="book"/>
   </zeroOrMore>
  </element>
 </define>

Or:

 category = element category{
  element title{text},
  category *,
  book*
 }

Note that in this case, the recursive reference to the "category" named pattern must be optional since otherwise the document would have to have an infinite depth!

!!Escaping named patterns identifiers in the compact syntax

In the last chapter "Chapter 4: Non XML Syntax" we have introduced the compact syntax and noticed that any reserved word could be used as element and attribute names. That's no longer the case for the identifiers of named patterns since they appear at the same position than the keywords.

If we had the funny idea to define a named pattern named "text", "start" or "element" for instance, the identifier of this named pattern could be confused with this keyword and in this case we need to escape the identifier by a leading "/". For instance to define (and by extension to make a reference) to a named pattern named "start", we would write:

 grammar{

 start = \start

 \start = element start { text }

 }

And in the XML syntax, this would translate as:

 <?xml version="1.0" encoding="UTF-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <start>
     <ref name="start"/>
   </start>
   <define name="start">
     <element name="start">
       <text/>
     </element>
   </define>
 </grammar>
