RELAX NG by Eric van der Vlist will be published by O'Reilly & Associates (ISBN: 0596004214)

You are welcome to use our annotation system to give your feedback.


The Case for Open Schemas

It's good to design extensible schemas, but they will only make an impact on developers who have the ability to extend our initial schema. A document valid per an extended flavor of our schema is likely to be invalid per our original schema.

By contrast, an open schema lets instances be extensible and allows the addition of content while still remaining valid against the original schema. 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 and using open schemas is quite challenging since it gives more power to the XML user and unexpected situations may result. The use open schemas also conflicts with some best practices of schema usage: a totally open schema would validate any well formed XML document and thus be totally useless. On the other hand, closed schemas violate the fundamental principle of extensibility of XML, the Extensible Markup Language.

There are several levels of openess from a totally closed schema where nothing unexpected can happen up to the most extreme case which would allow any well-formed document. In RELAX NG, name classes, introduced in the last chapter, Chapter 10: Namespaces, are the basic blocks which will let us build the wildcards needed to open a schema Ee'll take a closer look at name classes before presenting the constructions most often used in open schemas.

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 saw 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 shown in the last example can be used independently. 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. It would be meaningless to associate them with several name classes, unless you were doing it using choice. The "choice" element has provides a method of combining name classes. 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 which operates on specific element or attribute names. To define a name class which is "lib:name" or "hr:name", we can write:

 <element>
   <choice>
       <name>libname</name>
       <name>hrname</name>
   </choice>
   <ref name="anything"/>
 </element>

or:

 element lib:name | hr:name { text }

Note that the name name class expects a qualified name. These name classes can be combined pretty much as you like. We can also 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:dead</name>
     <except>
   </nsName>
   <ref name="anything"/>
 </element>

or:

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

This allows for future extension of the "hr" namespace, but doesn't allow the use of existing hr:* elements in ways we don't expect.


You are welcome to use our annotation system to give your feedback.
[Annotations for this page]
All text is copyright Eric van der Vlist, Dyomedea. During development, I give permission for non-commercial copying for educational and review purposes. After publication, all text will be released under the Free Software Foundation GFDL.