An Introduction to the Relaxer Schema Compiler
February 19, 2003
Michael Fitzgerald and ASAMI Tomoharu
Relaxer is a Java schema compiler for the XML schema languages RELAX NG and Relax Core. Using the Document Object Model, among other APIs, Relaxer generates Java classes based on schemas. It can also create classes based on XML document instances. The classes that Relaxer generates provide methods that allow you to access instances that are valid with regard to the compiled schemas, for use in your own programs that rely on the generated classes.
In addition to compiling schemas, Relaxer can also generate DTDs, RELAX NG schemas, Relax Core schemas, W3C XML Schema (WXS) schemas, and XSLT stylesheets. You can also create Java classes that support, among other things, SAX, Java Database Connectivity (JDBC), classic design patterns, such as composite and visitor, factories, and components for Enterprise JavaBeans, Remote Method Invocation (RMI), and the Java API for XML Messaging (JAXM). The generation of schemas, stylesheets, and Java classes will be covered in this article.
We will present a series of brief examples. In order to run these, you will need the SDK for Java 2 version 1.4 or higher (the latest SDKs are available for download from Sun's Java web site). Relaxer requires JAXP (Java API for XML Processing), which comes with version 1.4. You can run Relaxer with earlier versions of Java, such as 1.3.1, but it requires a separate download and installation of JAXP.
You will also need a copy of the latest version of Relaxer, available for download. The example documents discussed in this article are likewise available for download, and have all been tested on a Microsoft Windows XP Professional platform, running Java 2 version 1.4.1_01, and using a release candidate for Relaxer 1.0 (1.0RCb).
Installing Relaxer
Relaxer is easy to install. After you download the latest program archive file from
the
Relaxer site (called either setup.zip
or beta.zip
), and save it to
a working directory, run the Relaxer setup utility program by typing the following
command
at a shell or command prompt:
java -jar setup.zip
The utility walks you through a few installation steps, such as choosing the directory
where you want Relaxer installed. When you're done, you'll notice a couple files in
the
installation directory, one a shell script named relaxer
and another a batch
file named relaxer.bat
. The scripts help to keep Relaxer easy to run, because
it uses an absolute path to Relaxer.jar
, Relaxer's Java archive
The install program determines the classpath for Relaxer.jar
from the
installation process, and adds the classpath to the script and batch files. Place
the
Relaxer installation directory in your PATH variable, and you'll be ready to try out
the
examples. As long as the Relaxer installation directory is in your path, you won't
have to
set the classpath separately to run the examples if you use the script or batch file.
Generating Schemas
First off, here is a simple XML document that you can run through Relaxer's paces.
You'll
find this file, as well as all other files mentioned in this article, in the downloadable
example archive. Assuming that you are working in the directory
where you unzipped the examples, you should see a file called album01.xml
:
<?xml version="1.0" encoding="UTF-8"?> <album id="HANC-20241"> <title>The Best of Patsy Cline</title> <manufacturer>MCA Records, Inc.</manufacturer> <release>1985</release> <format>cassette</format> <condition>good</condition> </album>
Using the Relaxer script or batch file, you can generate a DTD with the -dtd
option in this way:
relaxer -dtd album01.xml
You can use the optional -verbose
option if you want to see a report on
Relaxer's activities. This command with the -dtd
option produce a single
artifact, a file named album01.dtd
in the current directory:
<!-- Generated by Relaxer 1.0RCb --> <!-- Tue Feb 04 13:45:40 PST 2003 --> <!ELEMENT manufacturer (#PCDATA)> <!ELEMENT title (#PCDATA)> <!ELEMENT release (#PCDATA)> <!ELEMENT album (title, manufacturer, release, format, condition)> <!ATTLIST album id CDATA #REQUIRED> <!ELEMENT artist (#PCDATA)> <!ELEMENT condition (#PCDATA)> <!ELEMENT format (#PCDATA)>
Relaxer constructs only a feasible content model in the DTD, based on some logical
guesswork. For example, Relaxer assumes that the id
attribute on
<album>
is a required attribute (#REQUIRED
), and that each
child of <album>
must occur exactly once. Relaxer can take more than one
instance as input, however, and as a result, can make more accurate guesses as to
what the
content model should be.
The following document, album02.xml
, is similar to album01.xml
,
but it does not have an id
attribute on the <album>
element.
It also tacks a <comments>
element on after
<condition>
:
<?xml version="1.0" encoding="UTF-8"?> <album> <title>The Best of the Sons of the Pioneers</title> <artist>The Sons of the Pioneers</artist> <manufacturer>RCA</manufacturer> <release>no date</release> <format>cassette</format> <condition>fair</condition> <comments>hold for Hank Vale</comments> </album>
Submit both album01.xml
and album02.xml
on the command line with
Relaxer, like this:
relaxer -dtd album02.xml album01.xml
This command produces a different DTD, one that reflects and balances the content
of both
documents. Relaxer automatically assumes the name of the first argument as the file
name for
the DTD (album02.dtd
):
<!-- Generated by Relaxer 1.0RCb --> <!-- Tue Feb 04 16:32:37 PST 2003 --> <!ELEMENT comments (#PCDATA)> <!ELEMENT manufacturer (#PCDATA)> <!ELEMENT title (#PCDATA)> <!ELEMENT release (#PCDATA)> <!ELEMENT album (title, artist, manufacturer, release, format, condition, comments?)> <!ATTLIST album id CDATA #IMPLIED> <!ELEMENT artist (#PCDATA)> <!ELEMENT condition (#PCDATA)> <!ELEMENT format (#PCDATA)>
Relaxer now assumes that the id
attribute is optional (#IMPLIED
)
and declares a <comments>
element, allowing one or zero occurrences
(?
) of <comments>
in the content model for
<album>
.
Similar command-line options allow you to build other kinds of schemas. For example,
to
create a RELAX NG schema from these instances, use the -rng
switch:
relaxer -rng album01.xml album02.xml
This generates the RELAX NG schema album01.rng
:
<?xml version="1.0" encoding="UTF-8" ?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <start> <ref name="album"/> </start> <define name="album"> <element name="album"> <optional> <attribute name="id"> <data type="token"/> </attribute> </optional> <element name="title"> <data type="token"/> </element> <element name="artist"> <data type="token"/> </element> <element name="manufacturer"> <data type="token"/> </element> <element name="release"> <data type="int"/> </element> <element name="format"> <data type="token"/> </element> <element name="condition"> <data type="token"/> </element> <optional> <element name="comments"> <data type="token"/> </element> </optional> </element> </define> </grammar>
Relaxer chooses the token
type from XML Schema datatypes (XSD) for elements
that could just as easily be string
types (<artist>, for example). This
is because the token
type
in XSD is a tokenized string that accepts single spaces between tokens.
Nonetheless, album01.rng
is simply Relaxer's attempt to write a feasible
schema. You are welcome, if not expected, to adjust this schema to suit your needs
and
preferences.
The next command creates a Relax Core schema, album01.rxm
:
relaxer -rxm album01.xml album02.xml
Lastly, the following command creates the WXS schema album01.xsd
by using the
-xsd
option:
relaxer -xsd album01.xml album02.xml
We won't show these last two schemas to you here, but you can look at them yourself with a text editor (they are in with the downloaded archive files). Creating XSLT stylesheets with Relaxer is just as easy as generating schemas.
Generating Stylesheets
With Relaxer's -xslt
option, you can construct an XSLT stylesheet that is
essentially an identity transform for the originating document or documents. The command
relaxer -xslt album01.xml album02.xml
produces the stylesheet album01.xsl
:
<?xml version='1.0'?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes" method="xml"/> <xsl:template match="album[title and artist and manufacturer and release and format and condition]"> <album> <xsl:attribute name="id"> <xsl:value-of select="@id"/> </xsl:attribute> <xsl:apply-templates/> </album> </xsl:template> <xsl:template match="manufacturer"> <manufacturer> <xsl:apply-templates/> </manufacturer> </xsl:template> <xsl:template match="format"> <format> <xsl:apply-templates/> </format> </xsl:template> <xsl:template match="release"> <release> <xsl:apply-templates/> </release> </xsl:template> <xsl:template match="artist"> <artist> <xsl:apply-templates/> </artist> </xsl:template> <xsl:template match="condition"> <condition> <xsl:apply-templates/> </condition> </xsl:template> <xsl:template match="title"> <title> <xsl:apply-templates/> </title> </xsl:template> <xsl:template match="comments"> <comments> <xsl:apply-templates/> </comments> </xsl:template> </xsl:stylesheet>
Each template contains literal result elements that mirror each node in the originating
document. If you process album01.xml
or album02.xml
with
album01.xsl
, you will get a result tree that fairly resembles the source
tree. It also gives you a good start for writing your own custom stylesheet.
You can also control the output of the XSLT generator by using the
-xslt.template
option in tandem with -xslt
. The
-xslt.template
option takes as a parameter an XSLT stylesheet that may be
annotated with Relaxer-specific attributes. When these attributes are processed by
Relaxer,
they will augment the location paths in a generated stylesheet, giving them a more
precise
context.
Consider the following stylesheet, album.xsl
:
<xsl:stylesheet version="1.0" xmlns:label="http://www.relaxer.org/xmlns/relaxer/xslt/label" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template label:match="album"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><title><xsl:apply-templates select="title"/></title></head> <body> <p><b>Release Date</b>: <xsl:apply-templates label:select="release"/><</p> </body> </html> </xsl:template> </xsl:stylesheet>
This stylesheet is intended to create a brief HTML document that contains an album's
title
and release date. The label
namespace marks attributes for Relaxer to process
specially. If you enter the command
relaxer -xslt -xslt.template:album.xsl album02.xml
Relaxer produces the following stylesheet (album02.xsl
):
<?xml version='1.0'?> <xsl:stylesheet version="1.0" xmlns:label="http://www.relaxer.org/xmlns/relaxer/xslt/label" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes" method="xml"/> <xsl:template label:match="album" match="album[title and artist and manufacturer and release and format and condition and comments]"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title> <xsl:apply-templates select="title"/> </title> </head> <body> <p> <b>Release Date</b>: <xsl:apply-templates label:select="release" select="release"/> </p> </body> </html> </xsl:template> </xsl:stylesheet>
Notice the value of the match
attribute in the template
element.
The location path now contains a predicate that is highly specific to documents that
have
the <album>
data model.
Generating Java Classes with Relaxer
Now let's generate some Java code with Relaxer. You can use an XML instance, a Relax
Core
schema, or a RELAX NG schema for code generation, but not WXS or DTDs. We'll use the
RELAX
NG schema produced earlier, album01.rng
. To generate Java source code from this
schema, enter this line at a command or shell prompt:
relaxer -java album01.rng
Note the -java
switch. Relaxer produces the following four Java files as a
result of this command:
-
Album.java
-
RStack.java
-
UJAXP.java
-
URelaxer.java
Album.java
is based on album01.rng
, and provides several
constructors for creating objects based on album01.rng
's data model. Among
others, Album.java
provides a default constructor with no arguments, another
that accepts a file as an argument (java.io.File
), and another that accepts a
SAX InputSource
argument (org.xml.sax.InputSource
).
Album.java
also provides a number of methods that you can use to access or
manipulate the content of a document that has the same content model. For example,
you can
use the getArtist()
and setArtist()
methods in
Album.java
to retrieve or change the content of the
<artist>
element in instances that are valid with regard to
album01.rng
.
Other methods in Album.java
include makeDocument()
, which creates
a DOM representation of the object modeled after the schema, and
makeTextDocument()
, which outputs a representation of the object as an XML
document.
The other three Java files that Relaxer produced -- RStack.java
,
UJAXP.java
, and URelaxer.java
-- were generated automatically
and support underlying functionality of Album.java
. The fields and methods in
these files are not user-accessible.
To easily examine the Java source that Relaxer produces, you can apply Javadoc to the source files with this line:
javadoc -d doc Album.java RStack.java UJAXP.java URelaxer.java
This command will place Javadoc's output files in the subdirectory doc
. If you
open the file doc/index.html
in a browser, you will be able to navigate through
the documentation of the classes to get a feel for all the fields, constructors, and
methods
that Relaxer created.
Compiling and Running Relaxer's Java Code
If you have a Java SDK installed, you can compile these Java files with javac
,
as follows:
javac Album.java
This command compiles Album.java
, RStack.java
,
UJAXP.java
, and URelaxer.java
. In the example archive, you will
also find a Java file that Relaxer did not create. It is called TestAlbum.java
.
Here is the source code for the program:
import java.io.File; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; /** * A small application that changes the content of * valid album type documents. * */ public class TestAlbum { public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException { if (args.length == 0) { System.out.println("Usage: java TestAlbum file.xml"); System.exit(1); } // Instantiate an Album object with a file Album album = new Album(new File(args[0])); // Print the XML representation of the document System.out.println("\nOriginal document:\n"); System.out.println(album.makeTextDocument()); // Change the content album.setId("ID-8406-4-R"); album.setTitle("Cool Water"); album.setArtist("The Sons of the Pioneers"); album.setManufacturer("RCA/BMG"); album.setRelease("no date"); album.setComments("none"); // Again, print the XML representation of the document System.out.println("\nUpdated document:\n"); System.out.println(album.makeTextDocument()); } }
Now compile and run this program.
javac TestAlbum.java java TestAlbum
The output for this program will look something like this:
Original document: <album id="HANC-20241"><title>The Best of Patsy Cline </title><artist>Patsy Cline</artist> <manufacturer>MCA Records, Inc.</manufacturer> <release>1985</release><format>cassette </format><condition>good</condition></album> Updated document: <album id="ID-8406-4-R"><title>Cool Water</title> <artist>The Sons of the Pioneers</artist> <manufacturer>RCA/BMG</manufacturer><release> no date</release><format>cassette</format> <condition>good</condition><comments>none </comments></album>
As you can see, the document content can be changed programmatically by calls to the
set
methods.
Conclusion
Relaxer has many other features which we cannot cover here. This article only deals
with a
few of its fundamental features, including schema, stylesheet, and Java generation.
To
continue exploring Relaxer, visit the Relaxer site,
where you will (soon) find a tutorial and reference manual. A book on Relaxer is also
available in Japanese. In addition, you will find many code examples in the
sample
subdirectory of the Relaxer distribution.