Extensible has become one of these buzzwords which have a both a very wide acceptance (everyone wants everything to be extensible, including computers, hifi systems, cars, houses and XML schemas) and are worn out to become almost meaningless (isn't it enough that I use  the eXtensible Markup Language to make my application extensible?). It might have been safer to keep away from such buzzwords, but since I have used it as the title of this chapter, the least I can do is to provide a definition of what an extensible schema is.

There are two different forms of extensibility for a schema: the schema itself can be extensible in that it i.e. made easy to derive variations through named patterns combinations or redefinitions or the schema can describe extensible documents where elements and attributes can be added without having to redefine the schema. The second case is often called an "open schema" or "open vocabulary".

Note that these two forms of extensibility are largely independent: a schema which is extensible through combinations and redefinitions can be perfectly strict and forbid the slightest variation in the instance documents while in a lesser attempt a schema which describes perfectly open documents can be difficult to extend without redefining most of its patterns.

In this chapter we will cover both paths to extensibility.

!!!Extensible schemas

Among the recipes to write extensible schemas, we can distinguish the recipes where the result is fixed and we are doing our best to write an extensible schema for an existing XML vocabulary (like if we were asked to cook the best "blanquette de veau") from the recipes where we have the freedom to act on the format itself and decide for instance when we will use elements or attributes, if order matters, ... (like if we were asked to cook the best veal meal).

!!Fixed result

In this case, the only parameter with which we can play is the way to define named patterns and this can do quite a difference in the same way that the definitions of classes in an object oriented implementation does have a lot of impact on its extensibility. In this section, we will see the major parameters to keep in mind when defining named patterns and start elements.

!Do provide a grammar and start element

Let's have a look back at our first schema:

 <?xml version="1.0" encoding="utf-8"  ?>
 <element xmlns="http://relaxng.org/ns/structure/1.0" name="library">
  <oneOrMore>
   <element name="book">
    <attribute name="id"/>
    <attribute name="available"/>
    <element name="isbn">
     <text/>
    </element>
    <element name="title">
     <attribute name="xml:lang"/>
     <text/>
    </element>
    <zeroOrMore>
     <element name="author">
      <attribute name="id"/>
      <element name="name">
       <text/>
      </element>
      <element name="born">
       <text/>
      </element>
      <optional>
       <element name="died">
        <text/>
       </element>
      </optional>
     </element>
    </zeroOrMore>
    <zeroOrMore>
     <element name="character">
      <attribute name="id"/>
      <element name="name">
       <text/>
      </element>
      <element name="born">
       <text/>
      </element>
      <element name="qualification">
       <text/>
      </element>
     </element>
    </zeroOrMore>
   </element>
  </oneOrMore>
 </element>

or:

 element library {
  element book {
   attribute id {text},
   attribute available {text},
   element isbn {text},
   element title {attribute xml:lang {text}, text},
   element author {
    attribute id {text},
    element name {text},
    element born {text},
    element died {text}?}*,
   element character {
    attribute id {text},
    element name {text},
    element born {text},
    element qualification {text}}*
  } +
 }

What happens if we want to derive a schema with an additional "id" attribute to the library element? That's simple: we have to take our schema, copy it and edit it as a new one! There is no extensibility at all since we cannot include a schema which has not a "grammar" element as a root.

The first thing to consider if we want a Relax NG schema to be extensible is to always use a complete form where the root element is a "grammar" element:

 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <start>
     <element name="library">
       <oneOrMore>
         <element name="book">
           <attribute name="id"/>
           <attribute name="available"/>
           <element name="isbn">
             <text/>
           </element>
           <element name="title">
             <attribute name="xml:lang"/>
             <text/>
           </element>
           <zeroOrMore>
             <element name="author">
               <attribute name="id"/>
               <element name="name">
                 <text/>
               </element>
               <element name="born">
                 <text/>
               </element>
               <optional>
                 <element name="died">
                   <text/>
                 </element>
               </optional>
             </element>
           </zeroOrMore>
           <zeroOrMore>
             <element name="character">
               <attribute name="id"/>
               <element name="name">
                 <text/>
               </element>
               <element name="born">
                 <text/>
               </element>
               <element name="qualification">
                 <text/>
               </element>
             </element>
           </zeroOrMore>
         </element>
       </oneOrMore>
     </element>
   </start>
 </grammar>

or:

 start =
    element library
    {
       element book
       {
          attribute id { text },
          attribute available { text },
          element isbn { text },
          element title { attribute xml:lang { text }, text },
          element author
          {
             attribute id { text },
             element name { text },
             element born { text },
             element died { text }?
          }*,
          element character
          {
             attribute id { text },
             element name { text },
             element born { text },
             element qualification { text }
          }*
       }+
    }

Note that for the compact syntax, "grammar" is implicit but that you still need to have a "start" pattern if you want to be able to redefine anything.

!Provide a fine enough granularity

Although the previous schemas (let's call them "russian-doll.rng" and "russian-doll.rnc") can be redefined, this redefinition is pretty ineffective since we are missing the granularity which would let us redefine only the "library" element and the best we can do is:

 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <include href="russian-doll.rng">
     <start>
       <element name="library">
         <attribute name="id"/>
         <oneOrMore>
           <element name="book">
             <attribute name="id"/>
             <attribute name="available"/>
             <element name="isbn">
               <text/>
             </element>
             <element name="title">
               <attribute name="xml:lang"/>
               <text/>
             </element>
             <zeroOrMore>
               <element name="author">
                 <attribute name="id"/>
                 <element name="name">
                   <text/>
                 </element>
                 <element name="born">
                   <text/>
                 </element>
                 <optional>
                   <element name="died">
                     <text/>
                   </element>
                 </optional>
               </element>
             </zeroOrMore>
             <zeroOrMore>
               <element name="character">
                 <attribute name="id"/>
                 <element name="name">
                   <text/>
                 </element>
                 <element name="born">
                   <text/>
                 </element>
                 <element name="qualification">
                   <text/>
                 </element>
               </element>
             </zeroOrMore>
           </element>
         </oneOrMore>
       </element>
     </start>
   </include>
 </grammar>

or:

 include "russian-doll.rnc"
 {
 start =
       element library
       {
          attribute id { text },
          element book
          {
             attribute id { text },
             attribute available { text },
             element isbn { text },
             element title { attribute xml:lang { text }, text },
             element author
             {
                attribute id { text },
                element name { text },
                element born { text },
                element died { text }?
             }*,
             element character
             {
                attribute id { text },
                element name { text },
                element born { text },
                element qualification { text }
             }*
          }+
       }
 }

In other words, here we need to redefine the whole schema and we have not gained in modularity since ulterior changes in the original schema would not be propagated into our resulting schema. To fix this, we need to provide a finer granularity in our definitions (we could say that the Russian doll design is level 0 of granularity and modularity!). This is often done through defining a named pattern per element (similar to the style of schema imposed by DTDs) and this would lead to a schema similar to the flat schema seen in "Chapter 5: Flattening our first schema":

 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <start>
     <ref name="library-element"/>
   </start>
   <define name="library-element">
     <element name="library">
       <oneOrMore>
         <ref name="book-element"/>
       </oneOrMore>
     </element>
   </define>
   <define name="author-element">
     <element name="author">
       <attribute name="id"/>
       <ref name="name-element"/>
       <ref name="born-element"/>
       <optional>
         <ref name="died-element"/>
       </optional>
     </element>
   </define>
   <define name="book-element">
     <element name="book">
       <attribute name="id"/>
       <attribute name="available"/>
       <ref name="isbn-element"/>
       <ref name="title-element"/>
       <zeroOrMore>
         <ref name="author-element"/>
       </zeroOrMore>
       <zeroOrMore>
         <ref name="character-element"/>
       </zeroOrMore>
     </element>
   </define>
   <define name="born-element">
     <element name="born">
       <text/>
     </element>
   </define>
   <define name="character-element">
     <element name="character">
       <attribute name="id"/>
       <ref name="name-element"/>
       <ref name="born-element"/>
       <ref name="qualification-element"/>
     </element>
   </define>
   <define name="died-element">
     <element name="died">
       <text/>
     </element>
   </define>
   <define name="isbn-element">
     <element name="isbn">
       <text/>
     </element>
   </define>
   <define name="name-element">
     <element name="name">
       <text/>
     </element>
   </define>
   <define name="qualification-element">
     <element name="qualification">
       <text/>
     </element>
   </define>
   <define name="title-element">
     <element name="title">
       <attribute name="xml:lang"/>
       <text/>
     </element>
   </define>
 </grammar>

or:

 start = library-element

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

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

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

 born-element = element born { text }

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

 died-element = element died { text }

 isbn-element = element isbn { text }

 name-element = element name { text }

 qualification-element = element qualification { text }

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

These new schemas (let's call them "flat.rng" and "flat.rnc") are more verbose but also much more extensible and to add our "id" attribute, we only need to redefine the "library" element:

 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <include href="flat.rng">
     <define name="library-element">
       <element name="library">
         <attribute name="id"/>
         <oneOrMore>
           <ref name="book-element"/>
         </oneOrMore>
       </element>
     </define>
   </include>
 </grammar>

or:

 include "flat.rnc"
 {
    library-element = element library { attribute id { text }, book-element+ }
 }

!Prefer to define named patterns for content rather than for elements

Although the previous result is much better, we still have to redefine the complete content of the library element to add our attribute and if we have reduced the problem we had wit our Russian doll model, we haven't eliminated it: if we change our main vocabulary and add a new attribute or element in "flat.rng", the modification will not be automatically taken into account in our schema and we will need to edit it.

This is because the extensibility of a named pattern doesn't cross element boundaries and since we have the boundary of the "library" element just included in our "library-element" named pattern, the content of this element isn't extensible:

[http://ibook/rngbook/flat.png]


To avoid this, we could have split our named patterns according to the content of the elements rather than the element themselves. We will then be able to add new content within the "library" element:


[http://ibook/rngbook/flat-content.png]

Generalizing this over the definition of all our elements would lead to a schema such as:

 <?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>
     <zeroOrMore>
       <element name="author">
         <ref name="author-content"/>
       </element>
     </zeroOrMore>
     <zeroOrMore>
       <element name="character">
         <ref name="character-content"/>
       </element>
     </zeroOrMore>
   </define>
   <define name="author-content">
     <attribute name="id"/>
     <element name="name">
       <ref name="name-content"/>
     </element>
     <element name="born">
       <ref name="born-content"/>
     </element>
     <optional>
       <element name="died">
         <ref name="died-content"/>
       </element>
     </optional>
   </define>
   <define name="born-content">
       <text/>
   </define>
   <define name="character-content">
     <attribute name="id"/>
     <element name="name">
       <ref name="name-content"/>
     </element>
     <element name="born">
       <ref name="born-content"/>
     </element>
     <element name="qualification">
       <ref name="qualification-content"/>
     </element>
   </define>
   <define name="died-content">
     <text/>
   </define>
   <define name="isbn-content">
     <text/>
   </define>
   <define name="name-content">
     <text/>
   </define>
   <define name="qualification-content">
     <text/>
   </define>
   <define name="title-content">
     <attribute name="xml:lang"/>
     <text/>
   </define>
 </grammar>

or:

 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 }*

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

 born-content = text

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

 died-content = text

 isbn-content = text

 name-content = text

 qualification-content = text

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

We can now take full advantage of the named pattern and, instead of redefining it, we can combine it with the "id" attribute:

 <?xml version="1.0" encoding="utf-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <include href="flat-content.rng"/>
   <define name="library-content" combine="interleave">
     <attribute name="id"/>
   </define>
 </grammar>

or:

 include "flat-content.rnc"

 library-content &= attribute id { text }

Of course, we have been lucky to have a situation where the extension could be done using a combination by interleave, but this is frequently the case, either when attributes need to be added or when elements need to be added but only when the relative order isn't significant for the schema. Otherwise, we would still have needed to redefine the pattern or to combine it by choice.

!!Free format

When we are free to define the vocabulary itself, we can give three principal guidelines to design extensible formats. The first one is independent of any schema language, the second one is specific to Relax NG to make sure we will be in a position to maximize the usage of combination through interleave and the third one a way to minimize the impact of the second on schemas which need to be converted into W3C XML Schema or DTD schemas.

!Be cautious about attributes

If Russian doll is degree zero in extensibility through schema redefinition or composition, attributes are degree zero in extensibility for XML information items! When choosing between elements and attributes, people often compare their relative easiness to be processed, styled or transform while the biggest difference is in their extensibility.

Independently of any XML schema language, when you have an attribute in an instance document, you are pretty much stuck with it and, save replacing it by an element, there is no way to extend it: you can't add any child element or attribute to it since it's designed to be and stay a leaf node. Furthermore, you can't extend its parent element to include a second instance of an attribute with the same name and are thus impacting not only the extensibility of the attribute but also the extensibility of the parent element.

To understand the reasons behind these limitations, it's worth looking back to the original use case for attributes in the XML 1.0 recommendation: attributes were originally designed to hold metadata about actual data which was to be stored in elements and the editors of XML 1.0 have considered that the lack of extensibility in XML attributes was not an issue for kind of "sticker notes" containing metadata.

Although most of the XML tools do not enforce this restriction and provide equal access to elements and attributes, these restrictions remain and it's wise to keep using attributes for what they've been designed for! In practice, my advise is to use attributes only when there is a good reason to do so and when the information is clearly metadata and that we have good reason to believe that this metadata will not have to be extended.

In our example of library, identifiers are good candidates, but even "available" should rather have been specified as an element: even though at first look this may be considered as metadata ("available" does not affect the description of a book if you are interested in that book only for literate reasons), other users looking at the "book" element under another angle may find this an important information attached to the book and may want to give it more structure to extend it to indicate if the book is available as a new or as a used item.

There is an exception when these rules must be relaxed: we've said in the previous chapter: "Chapter 11: Namespaces" that it wasn't a good idea to add foreign elements into a text only element since it was transforming its content model into mixed content. This rule isn't restricted to foreign elements and it's always risky to extend a text only element by adding elements while adding attributes will most of the time be unnoticed by existing applications.

The major reason to use attributes for information which is not clearly metadata is thus when extending elements with a text only (or text an attributes) content. In this case, the lack of further extensibility may be compensated by the short term gain in backward compatibility between the vocabularies before and after the extension.

!Be liberal on the relative order between children elements

Together with confusing the usage of elements and attributes, another bad habit taken during our few years of XML experience is the assumption that schemas should always enforce a fixed order between children elements or in other words that relative order between sub elements always matters.

This relative order is much less natural than we usually think. To draw a parallel with another technology, it's considered a bad practice to pay attention to the physical order of columns and rows in the table of a relational database. Furthermore, the dominant modeling methodology, UML, does not attach any order to the attributes of class (note that UML attributes are often used to represent not only XML attributes but also elements) and does not attach any order to relations between classes (unless specifically specified).

In fact, the main reasons behind this principle are limitations from DTDs and more recently from W3C XML Schema but there are strong reasons to believe that on the contrary when there is no special reasons, relative order between sub elements is a serialization detail and that we shouldn't bother users and applications with the unnecessary constraint of enforcing it.

With Relax NG, defining content models where the relative order between children elements is not significant is not only almost as simple as defining content models where it is significant (it's just a matter of adding "interleave" elements) but it is also more extensible since these content models can easily be extended through pattern combinations by "interleave".

Using them wherever it is possible is thus a way to put ourselves in a position where new elements and attributes can be added as shown in our example about the addition of the "id" attribute in the "library" element in the first sections of this chapter.

Note that together with the "element or attribute" question this issue is most controversial amongst XML experts. Technical constraints may in some cases justify enforcing element order in documents, most notably stream processing of huge documents where the presence of some information may allow to skip processing long content which would need to be buffered if this information came after the content. Other arguments which I find far from being obvious include the "disorder" carried by documents where element order is not enforced, easiness to read documents where you know where to find each element and even the fact that if the order isn't enforced users will be disoriented, confused and be at pain to choose an order.

While the "interleave" pattern works just fine most of the time, you'll need to keep in mind the restriction about the "interleave" pattern already mentioned in "Chapter 6: More Patterns": there can be only one "text" pattern in each "interleave" pattern. This restriction is hitting mixed content models found mainly in document oriented applications and may sometimes balance this advise of "being liberal with the relative order of elements".

!Don't be shy with containers

As mentioned, generalizing content models in which the relative order of children elements isn't significant is subject to limitations with other schema languages (notably DTD and W3C XML Schema). This can be a problem if you are using Relax NG as your main schema language and want to keep the possibility to generate DTD or W3C XML Schema schemas for the same vocabulary.

A way to limit the potential issues which may happen when generating schemas for languages which are less tolerant with the relative order of elements is to add elements acting as containers to insure that elements include either a text node, several elements which are not repeated or repeated elements with the same name.

Among the elements of our library, the book element is the only one which would be a problem for other schema languages if we wanted to switch its content model to "interleave". The "book-content" pattern would become:

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

or:

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

This would allow instance documents where "author" and "character" elements are mixed up with the other elements such as:

[http://ibook/rngbook/first-interleave.png]

and this is more than W3C XML Schema can support. In order to fix this and to define a schema which could more easily be translated into W3C XML Schema, we should add containers to isolate "author" and "character" elements from the other ones which cannot be repeated. The content of the "book" element would thus become:

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

or:

book-content =
   attribute id { text }
 & attribute available { text }
 & element isbn { isbn-content }
 & element title { title-content }
 & element authors { element author { author-content }* }
 & element characters { element character { character-content }* }

and it would validate elements such as:

[http://ibook/rngbook/first-interleave-container.png]

The relative order between the "isbn", "title", "authors" and "characters" is still not significant, but "author" and "character" elements are now grouped with their kind under containers and cannot interleave between the other elements and that's enough to make this schema much friendlier to schema languages with a lesser expressive power than Relax NG.

Note that even if these containers are not necessary for Relax NG, they are considered as a good practice by many XML experts who consider that they facilitate the access to "author" and "character" elements. The downside is that additional hierarchies are added and XPath expressions to qualify inner elements become more verbose: instead of writing "/library/book/character" to access to the "character" elements, we will have to write "/library/book/characters/character" and if this was repeated multiple times, this could lead to much longer expressions.

!! What about restricting existing schemas?

In the previous sections, we've focused on making schemas easy to extend through combination of named patterns and trying to limit the use of redefinition which leads to schemas with redundant pieces that are more difficult to maintain. However, extension is just one way of modifying a schema to adapt it to other applications and we often need, on the contrary, to restrict schemas to add new constraints or remove elements and attributes.

With Relax NG, designing schemas which are easy to restrict without redefinitions is much tougher than designing schemas which are easy to extend. The reason for this is that the only restriction which can be applied through combination is the combination of "notAllowed" patterns through interleave. As already shown in "Chapter 10: Creating Building Blocks", if the definition of the "died" element has been included in the named pattern "element-died", we can use this feature to remove this element from the schema:

 <?xml version="1.0" encoding="UTF-8"?>
 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
   <include href="library.rng"/>
   <define name="element-died" combine="interleave">
     <notAllowed/>
   </define>
 </grammar>

Or:

 include "library.rnc"
 element-died &= notAllowed

The rule of the thumb to write schemas easy to restrict is thus to increase the granularity of named pattern exactly as we've seen for writing extensible schemas.

Note that the distinction between defining named patterns for content rather than for elements which was important for writing extensible schemas becomes meaningless for defining schemas easy to restrict since interleaving a "notAllowed" pattern with an element or with its content leads in both case to a pattern that cannot be matched in any instance structure.

The issue of restricting schemas is tough enough to have motivated people to propose specific solutions and, outside of the scope of built-in features of Relax NG, Bob DuCharme has proposed a generic mechanism relying on annotations which are pre-processed to generate subsets of schemas. This will be described in next chapter: "Chapter 13: Annotating Schemas".

!!!The case for Open Schemas

That's fair enough to design extensible schemas like we've seen so far in this chapter, but this will only impact developers having the ability to extend our initial schema and a document valid per an extended flavor of our schema is likely to be invalid per our original schema.

By contrast, an open schema is about extensible instances and will allow additions of contents that remain valid per the original content. Of course, since the additions are unpredictable, the validation of their structure will be very lax, but still, extended documents will be considered as valid.

Designing open schemas is quite challenging since it deals with giving more power to the XML user and manage the unexpected situations which may result. Open schemas are also kind of antagonist with the very notion of "schema": a totally open schema would allow any well formed XML document and thus be totally useless. On the other hand, closed schemas are against the fundamental principle of extensibility of XML, the eXtensible Markup Language.

There are several levels of openness from level 0 which is a totally closed schema where nothing unexpected that has not been up-front designed can happen up to the top level which would allow any well formed document, but with Relax NG name classes introduced in last chapter ("Chapter 11: Namespaces") are the basic blocks which will let us build the wildcards needed to open a schema and we'll have a closer look to name classes before presenting the constructions most often used to open schemas.

!!More name classes

Let's first recap the name classes seen in the last chapter. We've seen how to use "anyName" to match any name from any namespace in the context of an element or an attribute:

  <define name="anything">
    <zeroOrMore>
      <choice>
        <element>
          <anyName/>
          <ref name="anything"/>
        </element>
        <attribute>
          <anyName/>
        </attribute>
        <text/>
      </choice>
    </zeroOrMore>
  </define>

or:

 anything = ( element * { anything } | attribute * { text } | text )*

Then we have seen how to remove specific namespaces from "anyName" using "except" and "nsName":

  <define name="foreign-elements">
    <zeroOrMore>
      <element>
        <anyName>
          <except>
            <nsName ns=""/>
            <nsName ns="http://eric.van-der-vlist.com/ns/library"/>
            <nsName ns="http://eric.van-der-vlist.com/ns/person"/>
          </except>
        </anyName>
        <ref name="anything"/>
      </element>
    </zeroOrMore>
  </define>

or:

 default namespace lib = "http://eric.van-der-vlist.com/ns/library"
 namespace local = ""
 namespace hr = "http://eric.van-der-vlist.com/ns/person"

 .../...

 foreign-elements = element * - (local:* | lib:* | hr:*) { anything }*

The two name class elements "except" and "nsName" associated in the last example can be used independently and if we want to define a name class for any name from the "lib" namespace, we can write:

 <element>
   <nsName ns="http://eric.van-der-vlist.com/ns/library"/>
   <ref name="anything"/>
 </element>

or:

 element lib:* { anything }

Elements and attributes have one and only one name and it would be meaningless to associate to them several name classes except as a choice. The "choice" element has thus been introduced to combine name classes and if we want to define a name class for any name from the "lib" or "hr" namespaces, we can write:

 <element>
   <choice>
     <nsName ns="http://eric.van-der-vlist.com/ns/library"/>
     <nsName ns="http://eric.van-der-vlist.com/ns/person"/>
   </choice>
   <ref name="anything"/>
 </element>

or:

 element lib:* | hr:* { anything }

Finally, there is also a name class to define single name and to define a name class which is "lib:name" or "hr:name", we can write:


 <element>
   <nsName ns=ns="http://eric.van-der-vlist.com/ns/person"/>
     <except>
       <name>lib:name</name>
       <name>hr:name</name>
     <except>
   </nsName>
   <ref name="anything"/>
 </element>

or:

 element lib:name | hr:name { text }

Note that the "name" name class is expecting a qualified name.

These name classes can be combined pretty much as you like and we can define a name class for any name from the "hr" namespace except the known elements:

 <element>
   <nsName ns=ns="http://eric.van-der-vlist.com/ns/person"/>
     <except>
       <name>hr:author</name>
       <name>hr:name</name>
       <name>hr:born</name>
       <name>hr:died</name>
     <except>
   </nsName>
   <ref name="anything"/>
 </element>

or:

 element hr:* - ( hr:author | hr:name | hr:born | hr:died ) { anything }

!!!Extensible And Open?

I have said in introduction to this chapter that the notions of "extensible" and "open" are largely independent and after all what we have seen we may even think that opening a schema may be a brake to its extensibility! Let's say we have written an open model for the content of our "element" element allowing foreign nodes:

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

or:

 book-content =
    attribute id { text }
  & attribute available { text }
  & element isbn { isbn-content }
  & element title { title-content }
  & element author { author-content }*
  & element character { character-content }*
  & foreign-nodes

So far, so good: we have applied independently the tips to build and extensible schema (using interleave and containers) and to define an open schema (through referencing a wildcard to allow foreign nodes). Unfortunately, if our schema is open, it's not very extensible any longer!

Imagine I want to add a couple of XLink attributes to define a link toward a web page, I can't combine this new attribute by interleave since this new attribute would be considered as duplicated with the implicit definition of "xlink:href" already contained in our "foreign-nodes" wildcard.

The situation is pretty much the same with the addition of new elements. If I want to add for instance an optional "dc:copyright" element I can do it but the constraint applied to this element will be in conflict with the lax definition of "dc:copyright" implicitly contained in our "foreign-nodes" wildcard and if our new constraint is not met, Relax NG will still find a match for a bogus dc:copyright element in the wildcard.

Does that mean that open schemas cannot be extensible? Yes and no! It's a fact that the wildcards make open schemas less extensible, but that only means that we must extend our schemas before opening them. To come back to our example, we'd better write a closed schema first:

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

or:

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

We can then carefully keep this closed schema in a first document and extend it by inclusion and combination to become open as we've seen in last chapter:

  <include href="closed-schema.rng"/>
  <define name="book-content" combine="interleave">
    <ref name="foreign-nodes"/>
  </define>

or

 include "closed-schema.rnc"
 book-content &= foreign-nodes

Applications would then use the open schema (after inclusion and combination) with the same benefit than when the schema was natively open but the "closed-schema" would be available to extend the content model, redefine the "foreign-nodes" wildcard and use it to open the schema again.
