Menu

Designing a New Schema with XML Design Patterns

June 4, 2003

Kyle Downey

Introduction

In " Architectural Design Patterns for XML Documents" I proposed a catalog of XML schema-design patterns. In keeping with the idea behind design patterns, they were culled from schemas in common use today, but it's still good to take them for a spin and apply them to a real project. In this article I'll do just that. The project in question is the design of an XML-based type library format. If you've had exposure to Microsoft COM or Mozilla's XPCOM, you're probably familiar with their binary TLB (MS) and XDT (Mozilla) formats that define the available operations and interfaces for a package of portable components. An interpreted language such as JavaScript can use these definitions as cheat sheets to find out what operations and parameters are available to call on-the-fly.

Each component model allows you to write components in many languages, but these binary formats are friendly to just one language: C. In this article we're going to start a project to design an XML format that could be generated from IDL, TLB, XDT or any other representation of a portable component and then be read by any language that supports XML and further manipulated to generate GUI tools, documentation, and more.

We could imagine an instance document like this that defines an interface Hello with a single operation, string sayHello(in string personName):

<tlx:typelib

  xmlns:tlx="http://schema.amberarcher.com/polaris/tlx.html">

  <tlx:interface name="GreetingFactory"

    id="8bb35ed9-e332-462d-9155-4a002ab5c958">

    <tlx:operation name="sayHello" type="string">

      <tlx:in name="personName" type="string"/>

    </tlx:operation>

  </tlx:interface>

</tlx:typelib>

Dynamic or Static Document Structure

The Dynamic Document pattern, which suggests not writing a schema and just following the data structure of the program, doing the parsing using .NET Marshalling or the JavaBeans long-term persistence from JDK 1.4, could let us end the article right here. It should be clear immediately this option is right out: the point of the project is to create a stable, portable format, and we don't want it influenced by changes in the language we use to parse or generate it. Also, unlike Ant, say, this is a fairly self-contained project, and we don't need to allow arbitrary people to write extensions without having to incorporate them into the schema.

Composition/Self-Documenting Files

There are two types of composition: reuse of your own schema elements and reuse of other people's schema elements. We'll endeavor to do both for this project. To start, we need some requirements:

  1. We want to represent interfaces and operations on those interfaces.
  2. We want to allow for vendor/format-specific extensions.
  3. We want to provide documentation for those same interfaces and operations.
  4. Since this will be generated code, we want to provide some information about who generated it and when, as well asthe original source.

Requirement (1) is core: it wouldn't be a type library without defining the interfaces and operations, and we can't borrow this from anywhere else. Requirement (2) is also central, but one thing we have to consider for composition is who controls the schema and how fast it changes. The idea of an interface and a method call will not change over the lifetime of this schema, but vendors come and go. We should put Mozilla and Microsoft specific information in their own schemas and compose them into the larger schema.

Requirement (3) screams for some petty schema larceny. Documentation nowadays should be hypertext and convertible into other formats. Both DocBook/XML and XHTML's more basic elements fit the bill. Since HTML is familiar to most programmers, we'll choose to compose XHTML.

Finally, requirement (4) could be filled with RDF and the Dublin Core. There's no point in reinventing file metadata, and there are plenty of tools out there for reading RDF. So with our tools chosen, let's go back to our first example, and mark up the document using all our borrowed schemas:

<tlx:typelib 

   xmlns:tlx="http://schema.amberarcher.com/polaris/tlx.html">

  <!-- for generated code, we mostly want to know what

      version of the program created it, when it was last

      (re)generated, and what was the original source;

      Dublin Core offers all of these for our use -->

    <tlx:metadata>

      <rdf:RDF 

          xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

          xmlns:dc="http://purl.org/dc/elements/1.1/">

        <rdf:Description>

          <dc:creator>xpidl2tlx 1.0</dc:creator>

          <dc:date>2003-05-02</dc:date>

          <dc:source>file:c:/oss/mozilla/modules/libreg/

xpcom/nsIRegistry.idl</dc:source>

        </rdf:Description>

      </rdf:RDF>

    </tlx:metadata>



    <tlx:interface name="GreetingFactory"

         id="8bb35ed9-e332-462d-9155-4a002ab5c958">



      <!-- documentation is nested inside the object being

          documented; the text chosen here is from the

          javadoc-type description in the original IDL -->

        <tlx:documentation>

          <xhtml:p

              xmlns:xhtml="http://www.w3.org/1999/xhtml">

              Interface for creating custom greetings.

          </xhtml:p>

        </tlx:documentation>



        <tlx:operation name="sayHello" type="string">

          <tlx:documentation>

            <xhtml:p

                xmlns:xhtml="http://www.w3.org/1999/xhtml">

                Returns a greeting that incorporates the

                person's name.

            </xhtml:p>

          </tlx:documentation>



          <tlx:in name="personName" type="string">



            <!-- documentation is nested inside the

                parameter being documented -->

              <tlx:documentation>

                <xhtml:p 

                    xmlns:xhtml="http://www.w3.org/1999/xhtml">

                  name of the person to greet

                </xhtml:p>

              </tlx:documentation>



          </tlx:in>

        </tlx:operation>

    </tlx:interface>

</tlx:typelib>

What have we gained? It seems like a lot of added complexity and bulk. As in the original pattern catalog, the reason we use XHTML or RDF when we have a need for their services is to get ourselves out of the business of defining tags in that domain. We care about typelibs, not hypertext. If someone wants to put in a hyperlink in the documentation, we don't need to define a tag. XHTML has it already. Also, by using a well-known schema, it becomes easier to use shared tools: an RDF-aware document-management system would immediately recognize the creation date and creator, while it might ignore that extra information if it were buried in our own "create-date" and "creator" tags.

We've also satisfied another common schema pattern along the way: we've made our format self-documenting. While not child's play, it's still much easier to write a stylesheet to convert the above document to HTML than it is to, say, parse XPIDL and javadoc-style tags to produce it. A GUI browser tool can also provide human descriptions for cryptically-named functions and parameters.

Multipart Files

Our last architectural pattern is Multipart Files. This pattern suggests we should offer our document creators a way to compose a single, coherent document out of many. Type libraries define interfaces, and interfaces can inherit from other interfaces. We may thus want to define our common base interfaces in one file, much like we'd define and #include a header in C or C++. We have two choices for implementing this feature: borrow or invent. We're on a roll borrowing, so here's what it would look like using XInclude, the W3C Candidate Recommendation for this purpose:

<tlx:typelib

     xmlns:tlx="http://schema.amberarcher.com/polaris/tlx.html"

     xmlns:xi="http://www.w3.org/2001/XInclude">

   <xi:include href="base.tlx"/>

   <!-- ... -->

 </tlx:typelib>

Next Step: Building the Schema

We've gone from a blank sheet of paper and a couple requirement,s to sketches of the file format we want to support, plus a set of patterns to guide us: Multipart Files, Self-Documenting Formats, and Composition. There are a number of capable XML Schema tutorials out there, so the focus here will be on XML Schema details to support incorporating other schemas into our design.

Implementing Composition

We're composing three different schemas for this design, so we need to import them so we can refer to their elements.

<schema  targetNamespace=

  "http://schema.amberarcher.com/polaris/tlx.html"

  xmlns:tlx="http://schema.amberarcher.com/polaris/tlx.html"

  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

  xmlns:xhtml="http://www.w3.org/1999/xhtml"

  xmlns:xi="http://www.w3.org/2001/XInclude"

  xmlns="http://www.w3.org/2001/XMLSchema"

  elementFormDefault="qualified">



  <import

    namespace="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

    schemaLocation="http://dublincore.org/documents/2002/07/31/

dcmes-xml/dcmes-rdf.xsd"/>



  <import

    namespace="http://www.w3.org/1999/xhtml"

    schemaLocation="http://schema.amberarcher.com/polaris/w3c/

xhtml1.1.xsd"/>



  <import

    namespace="http://www.w3.org/2001/XInclude"

    schemaLocation="http://schema.amberarcher.com/polaris/w3c/

xinclude.xsd"/>

For the W3C schemas, the first snag I hit was that there's not yet an authoritative URI for either the XHTML 1.1 or XInclude XML schemas. I had to download the zip files that come with the specifications and put them up on my server. Since these are snapshots of the versions at this time, I do not recommend that you reuse my URLs (my ISP will thank you as well); they may not stay up-to-date.

With the full contents of these three namespaces at our disposal, we can reference them. Let's define our top-level element, typelib, and say it's a sequence of two optional elements and an unbounded set of one or more interfaces. That looks like this:

<element name="typelib">

  <complexType>

    <sequence>

      <element ref="tlx:metadata" minOccurs="0"/>

      <element ref="xi:include" minOccurs="0"/>

      <element ref="tlx:interface" maxOccurs="unbounded"/>

    </sequence>

  </complexType>

</element>



<element name="metadata">

  <complexType>

    <sequence>

      <element ref="rdf:RDF"/>

    </sequence>

  </complexType>

</element>

We use <element ref="xxx:yyy"> whenever we want to link to another schema element. We have to define a namespace prefix (xi or rdf in this case) to scope the name, and then we can pick any element in that schema. Note that we don't necessarily have to choose the top-level element, as we'll see in the next section.

Implementing multi-part documents on the schema side was just one line to include <xi:include>. Supporting it on the document processing side may be easy if your XML Parser has native XInclude support, or you may have to write it yourself using one of the known implementations. There are versions for Java, .NET, and C/C++.

The XHTML Snag

What I intended to do with XHTML was to reuse its <div> and <p> as allowed container elements for documentation so that our embedded documentation could have hyperlinks and other mark-up. This can be done with W3C XML Schema like this:

<element name="documentation">

  <complexType>

    <choice>

      <element ref="xhtml:p"/>

      <element ref="xhtml:div"/>

    </choice>

  </complexType>

</element>

Easy, right? I completed the schema, validated the W3C XML Schema code itself without trouble, and then tried to validate an instance document using XSV. I got an explosion of validation errors, all from what seemed to be the XHTML 1.1 schema itself; listserve and newsgroup messages on the topic seemed to indicate that this problem has been around a while. I tried to find variants of the schema published elsewhere, but the only one I could dig up was based on a draft version of W3C XML Schema.

Although the XHTML working group has moved on to XHTML 2.0, my understanding is that the finished work for 1.1 is in the DTD, while Modularization of XHTML in XML Schema is still just a W3C working draft as of 9 December 2002. I've written the lead author of the specification and at the time of writing have yet to hear back. I think the lesson for anyone applying Composition is to be very careful about choosing stable specifications. Don't make assumptions about the availability of a compliant W3C XML Schema for any incomplete specification until you've tested it yourself. The flip-side of being able to rely upon someone else to define the elements for your problem domain is that you have to depend upon them.

For now the test schema for TLX has the XHTML import commented out and the documentation tag is declared more simply as:

<element name="documentation" type="string"/>

With these changes to the test schema, and changes to the example document, XSV blessed the document as valid.

Using inheritance to share documentation

Even though I'm not yet able to support validation and embedded XHTML in the documentation at the same time, there's one final nice touch to put in the schema. A number of elements can have embedded documentation: interfaces, operations, and parameters. We can use inheritance in XML Schema to prevent duplication in our new schema. First we define a complexType named documentableType. We declare it abstract because we don't want any elements that directly use this type, just derived types:

<complexType name="documentableType" abstract="true">

  <sequence minOccurs="0">

    <element ref="tlx:documentation"/>

  </sequence>

</complexType>

To finish we just have to derive the complexTypes for each of our elements from documentableType using W3C XML Schema's <extension> tag.

<element name="interface">

  <complexType>

    <complexContent>

      <extension base="tlx:documentableType">

        <sequence>

          <element ref="tlx:operation" maxOccurs="unbounded"/>

        </sequence>

        <attribute name="name" type="string"/>

        <attribute name="id" type="string" use="optional"/>

      </extension>

    </complexContent>

  </complexType>

</element>

Conclusions

When you set out to design your own XML Schema, you do not need to start from scratch. You can use either patterns exemplified by the growing body of working schemas on the Internet, from the W3C to OASIS, or you can directly reuse their elements through Composition. It's worth the effort, because you can get the value of a language specific to your domain without the trouble of writing your own parser, and the end result can be used across multiple languages with ease.