Skip to main content

skip to main content

developerWorks  >  XML  >

XML Matters: Kicking back with RELAX NG, Part 1

Doing better than W3C XML Schema

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Introductory

David Mertz (mertz@gnosis.cx), Idempotentate, Gnosis Software, Inc.

01 Feb 2003

RELAX NG schemas provide a more powerful, more concise, and semantically more straightforward means of describing classes of valid XML instances than do W3C XML Schemas. The virtue of RELAX NG is that it extends the well-proven semantics of DTDs while allowing orthogonally extensible datatypes and easy composition of related instance models. David takes a first look at RELAX NG in this, the first installment of a three-part series.

I have long been wary of W3C XML Schema, and to some extent of XML itself. A jumble of companies and groups with divergent interests and backgrounds cobbled together W3C XML Schema by throwing in a little bit of everything each party wanted, creating a typical committee-designed, difficult-to-understand standard. In fact, I have so many reservations that I generally recommend sticking with DTDs for validation needs, and filling any gaps strictly at an application level.

About a month ago, however, I started taking a serious look at RELAX NG. Like many readers, I had heard of this alternative schema language previously, but I had assumed that RELAX NG would be pretty much more of the same, with slightly different spellings. How wrong I was. RELAX NG is simply better than either W3C XML Schemas or DTDs in nearly every way! In fact, RELAX NG's ability to support unordered (or semi-ordered) content models answers most of my prior concerns about the mismatch between the semantic models of OOP datatypes and the linearity of XML elements.

Other articles in this RELAX NG series

This article is the first of three XML Matters installments that discuss RELAX NG. This installment will look at the general semantics of RELAX NG, and touch on datatyping. The next installment will look at tools and libraries for working with RELAX NG. The final installment will discuss the RELAX NG compact syntax in more detail.

Semantic model

The semantics of RELAX NG are enormously straightforward -- in this respect, they are a natural extension of DTD semantics. What a RELAX NG schema describes is patterns that consist of quantifications, orderings, and alternations. In addition, RELAX NG introduces a pattern for unordered collection, which neither DTDs nor W3C XML Schemas support (SGML does, but less flexibly than RELAX NG). Moreover, RELAX NG treats elements and attributes in an almost uniform manner. Element/attribute uniformity corresponds much better with the conceptual space of XML than does the rigid separation in both DTDs and W3C XML Schemas. In actual design, the choice between use of an attribute and an element body is frequently underdetermined by design considerations and/or is contextually sensitive.

The quantifications available to RELAX NG are identical to those in DTDs. Any pattern may be conditioned as <oneOrMore>, <zeroOrMore>, or <optional>; these correspond to the DTD quantifiers +, *, and ? (also used in regular expressions and elsewhere), respectively. In fact, the RELAX NG compact syntax uses the very same quantifiers that are used in DTDs. Admittedly, these very general quantifiers make it more difficult to state specific cardinality constraints, as you can with the W3C XML Schema minOccurs and maxOccurs attributes. I would not mind seeing a later revision of RELAX NG that incorporates more flexible cardinality constraints. However, it is easier to work around these limits using named patterns than it is to do so in DTDs.

Ordering multiple patterns is just a matter of listing the patterns in an order. But a sequence of patterns at the same level can be given different semantics using the <choice>, <group>, or <interleave> elements. The <group> tag is used in just the same way that parentheses are in DTDs. By itself, a <group> element doesn't mean anything, but when used inside <choice> or <interleave> elements, a group acts as one pattern rather than several. The <choice> element expresses simple alternation between contained patterns. The <interleave> element, however, lets you intersperse patterns while obeying the cardinality of each contained pattern. For example, suppose a library patron has a name, ID number, and possibly some checked out books -- for these purposes, we do not care which order the features are listed in. A book, in turn, can be identified by either title or ISBN (but not both -- perhaps an unrealistic example) . A RELAX NG description could look like this:


Listing 1. Library patron schema
                

<element name="patron"
         xmnln="http://relaxng.org/ns/structure/1.0">
  <interleave>
    <element name="name"><text/></element>
    <element name="id-num"><text/></element>
    <zeroOrMore>
      <element name="book">
        <choice>
          <attribute name="isbn"/>
          <attribute name="title"/>
        </choice>
      </element>
    </zeroOrMore>
  </interleave>
</element>

Understanding this schema is almost a matter of simply reading it aloud. But let's first look at the compact syntax that corresponds to this XML syntax:


Listing 2. Library patron compact syntax
                

element patron {
  element name { text }   &
  element id-num { text } &
  element book {
    (attribute isbn { text } |
     attribute title { text } )
  }*
}

In fact, you cannot describe the valid patron records using either DTDs or W3C XML Schema, at least not without elaborate contortions and/or compromises in precision. For example, here are two valid records:


Listing 3. Valid patron records
                

<patron>
  <book isbn="0-528-84460-X"/>
  <name>John Doe</name>
  <id-num>12345678</id-num>
  <book title="Why RELAX is Clever"/>
</patron>
<patron>
  <id-num>09876545</id-num>
  <name>Jane Moe</name>
</patron>

But Listing 4 is invalid in a way that other schemas cannot generically describe (attributes can only be optional or required in DTDs and W3C Schema, not interrelated):


Listing 4. Invalid patron records
                

<patron>
  <name>John Doe</name>
  <id-num>12345678</id-num>
  <book title="Why RELAX is Clever" isbn="0-528-84460-X"/>
  <book/>
</patron>

Moreover, the ability of a RELAX NG schema to define an unordered collection of name, ID number, and book(s) answers my complaint -- when discussing YAML and elsewhere -- that XML imposes arbitrary order on data that is not inherently sequential.

An interesting upshot of the general behavior of interleaving is that you can mix text/PCDATA sections with subelements, and control the number (cardinality) of text blocks allowed. For example, you could allow a single contiguous flow of PCDATA to occur anywhere among some optional or required tags.



Back to top


Uniformity of attributes and elements

A quite common usage scenario for XML is an element that can contain either a special attribute or a collection of subelements (or PCDATA content), but not both. For example, the gnosis.xml.pickle serialization format defines heterogeneous lists similar to this:


Listing 5. gnosis.xml.pickle-ish list fragment
                

<list>
  <item type="string" value="Bar"/>
  <item type="list">
    <item type="numeric" value="17"/>
    <item type="None"/> <!-- None is singleton w/o value -->
  </item>
</list>

A given <item> contains subelements only if it does not contain a value attribute, and vice versa. Using a DTD, we could approximate the rule as:


Listing 6. Approximate DTD for pickle
                

<!ELEMENT list (item+)>
<!ELEMENT item (item*)>
<!ATTLIST item
    type  (None | string | numeric | list) #REQUIRED
    value CDATA #IMPLIED >

That DTD will match the prior XML instance document, but it will also falsely match:


Listing 7. Invalid false-match to DTD
                

<list>
  <item type="None" value="Some">
    <item type="string" value="More"/>
  </item>
  <item type="list"/>
</list>

A W3C XML Schema is enormously arcane to start with, but in the end has no ability to describe anything more specific than the DTD does, for this case. For example, the following is a schema for the simplified case where an <item> may simply contain PCDATA, rather than subelements:


Listing 8. Approximate W3C XML Schema
                

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="list">
  <xsd:element name="item" minOccurs="1" maxOccurs="unbounded">
    <xsd:complexType>
      <xsd:simpleContent>
        <xsd:extension base="xsd:string">
          <xsd:attribute name="type" type="xsd:string"/>
          <xsd:attribute name="value" type="xsd:string"
                         use="xsd:optional"/>
        </xsd:extension>
      </xsd:simpleContent>
    </xsd:complexType>
  </xsd:element>
</xsd:element>

The problem here with both DTDs and W3C XML Schemas is that they make attibutes too special, and treat them very differently from elements. RELAX NG, on the other hand is uniform (the simplified case of PCDATA is presented here to skip named patterns, for now):


Listing 9. RELAX NG XML syntax for pickle
                

<element name="list" xmlns="http://relaxng.org/ns/structure/1.0>
  <oneOrMore>
    <element name="item">
      <choice>
        <text/>
        <attribute name="value"/>
      </choice>
    </element>
  </oneOrMore>
</elment>

In compact syntax, we could write:


Listing 10. RELAX NG compact syntax for pickle
                

element list { element item { attribute type {text},
                      (attribute value {text} | {text}) }+ }



Back to top


Named patterns

Nested (and context sensitive) definitions of subpatterns are only one style available in RELAX NG. You may also work with named patterns within a grammar. Moreover, by using a grammar, a RELAX NG schema can explicitly indicate a root element for validation. A grammar contains a root <grammar> element, a single <start> element, and zero or more <define> elements. Notably, <define> elements can contain recursive elements.

A sample grammar illustrates how definitions and references are used. Let's define the nested <item> tags that we skipped over in the above examples:


Listing 11. Improved RELAX NG grammer for pickle
                

<grammar xmlns="http://relaxng.org/ns/structure/1.0>
  <start>
    <element name="list">
      <ref name="items"/>
    </element>
  </start>
  <define name="items">
    <oneOrMore>
      <element name="item">
        <choice>
          <ref name="items"/>
          <attribute name="value"/>
        </choice>
      </element>
    </oneOrMore>
  </define>
</grammar>

This finally gives us an accurate validation constraint for the attribute-or-subelements serialization form described above. For a real-life case, we would typically define more named patterns than this; each can freely refer to others (within quantifications, choices, and so on).



Back to top


Datatypes

While W3C XML Schemas have a complex collection of datatypes built in, and DTDs effectively have no datatyping at all, RELAX NG includes a completely modular and expandable datatyping system. Most commonly, RELAX NG users will simply use the entire collection of datatypes that accompany W3C XML Schemas. As with a specification of a namespace, a datatype library is found in the most immediate surrounding element that defines the attribute datatypeLibrary. So, for example, you could define the datatype library with every data value:


Listing 12. Verbose specification of datatypes
                

<element name="foo">
  <choice>
    <data type="integer"
      datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
    <data type="float"
      datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
  </choice>
</element>

A more parsimonious approach, however, would be to indicate a datatype library at the top level. If a specific data value needs to follow a different datatype library, you can override that within that individual <data> tag. For example:


Listing 13. Concise specification of datatypes
                

<element name="foo"
      datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
  <choice>
    <data type="integer"/>
    <data type="float"/>
  </choice>
</element>

A nice feature that you have already seen is that defining compound datatypes uses the exact same patterns as definitions of elements and attributes. A choice is generally the most applicable pattern element, but others (including <list>, which is discussed below) are sometimes applicable.

Enumerations may be specified using the <value> element instead of a <data> element. For example:


Listing 14. Enumerated values using choice
                

<element name="foo">
  <choice>
    <value type="integer">1</value>
    <value type="integer">2</value>
    <value type="integer">3</value>
    <value type="string">infinity</value>
  </choice>
</element>

Some datatypes in libraries can allow parameterization. If this option exists, you can narrow the range of acceptable values (without limiting all the way to an explicit enumeration). The power is identical to that in a library -- i.e. to what W3C XML Schema allows. For example:


Listing 15. Parametric specification of datatype
                

<element name="foo">
  <data type="string">
    <param name="maxLength">10</param>
  </data>
</element>

When you utilize a datatype library, you can still construct somewhat customized datatypes using the <list> element. A list is simply a whitespace-separated sequence of tokens, each matching some datatype. As elsewhere, lists can describe either element or attribute contents. For example, suppose you would like an attribute to contain a collection of one or more integer values:


Listing 16. Compound specification of datatype
                

<element name="foo">
  <attribute name="numbers">
    <list>
      <oneOrMore>
        <data type="integer"/>
      </oneOrMore>
    </list>
  </attribute>
</element>

A matching document would be:

<foo numbers="1 2 3 988765"/>

An invalid document example would be:

<foo numbers="word"/>



Back to top


What next?

There is a bit more to RELAX NG than this article has touched on -- but surprisingly little. It is quite remarkable just how much power can be had with so few simple concepts. The next two installments of XML Matters will touch on issues like merging grammars, infoset augmentation (or lack thereof), fudging cardinality constraints, and a few other semantic concepts. But for the most part, we will focus on tools and the compact syntax.



Resources



About the author

author

David Mertz, in his gnomist aspirations, wishes he had coined the observation that the great thing about standards is that there are so many to choose from. But then, he is also fuzzy on OS design. David may be reached at mertz@gnosis.cx; his life pored over at http://gnosis.cx/dW/. Suggestions and recommendations on this, past, or future columns are welcomed.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top