Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Long Term Persistence of JavaBeans Components: XML Schema

 

Long Term Persistence of JavaBeans Components:
XML Schema

The persistence scheme added in v 1.4 uses instances of the XMLEncoder class to write out files representing JavaBeans components (beans). Every file written by XMLEncoder uses the same XML schema, regardless of the beans the file contains. In this document we describe this schema so that implementations other than XMLEncoder and its corresponding reader, XMLDecoder, can be used to write and read compatible files.

This document presents the basic elements of each XML archive, followed by the tags necessary to represent objects. Next comes a section of abbreviations -- tags that aren't strictly necessary to write out an XML archive, but that make the archive shorter and easier to read. The final sections describe the top level of the XML archive, which can refer to properties of the decoder, and give a DTD for the XML schema.

You can find an example XML archive here: Browse.xml. This example is an archive of a simple application that accepts a URL and, using a JEditorPane, displays the HTML from that URL. You can read the archive and run the application using the following code:

try {
    XMLDecoder d = new XMLDecoder(
                       new BufferedInputStream(
                           new FileInputStream("Browse.xml")));
    d.readObject();
    d.close();
} catch (IOException e) {
    ...handle the exception...
}

For a ready-made program that reads XML archives, see TestInput.java. Sample scripts for running it on Win32 and UNIX are in xml.bat and xml, respectively.


Basic Elements

Each document begins with the optional XML preamble, specifying the version of the XML specification and encoding it uses. The preamble is followed by a single element with the <java> tag. 
<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
  ...objects go here...
</java>

The <java> element contains two informational attributes (not currently used by XMLDecoder): the version attribute, which records the version of the Java platform that was used to write the archive, and the class attribute, which specifies the class of the decoder for which the document was written. The objects that the archive contains make up the body of this element and appear in the order they will be returned by calls to the decoder's readObject method.


Objects

Objects are represented by the sequence of method calls that will be used to create them. Each element in the XML document represents a method call that either creates an object (an expression) or has a side effect on an object (a statement). Strings are treated as special kinds of expressions. Identifiers name objects so they can be referred to after their creation.


Strings

Strings are the atomic expressions of the XML document. The characters in a string form the body of an element with the <string> tag. For example, the string "Hello, World" is represented by the following XML code:

<string>Hello, World</string>

The '<' and '&' characters are represented by the &lt; and &amp; escape sequences.


Expressions and Statements

Recall that expressions are method calls that return a value, and statements are method calls that don't. Each expression can be represented by an element with the <object> tag; each statement is represented by the <void> tag. For both <object> and <void> tags, the method attribute specifies the name of the method to be called when the document is read.

The class attribute can be used in <object> tags to specify a class as the target of a static method. Constructors are represented as static methods that have the name new.

When an expression or statement contains expressions, the contained expressions are used as arguments to the method represented by the outer expression or statement. For example, to create an instance of the JButton class we can write the following:

<object class="javax.swing.JButton" method="new">
  <string>Press me</string>
</object>
Because the enclosed element represents a string, which is an expression, the value "Press me" is used as an argument to the constructor of the JButton class. The equivalent code written in the Java programming language ("Java code") would be:
new JButton("Press me");
The default method name is new. It can therefore be omitted, yielding the equivalent:
<object class="javax.swing.JButton">
  <string>Press me</string>
</object>
When statements appear inside the body of an expression, the statements are applied to the enclosing object after it is created. To execute a method with a side effect on an object we place the method inside the object to be affected. For example:
<object class="javax.swing.JButton">
  <void method="setText">
    <string>Hello, world</string>
  </void>
</object>

This corresponds to the following code fragment:
JButton b = new JButton();
b.setText("Hello, world");
If an expression should not be used as an argument to the enclosing method, it should be represented with the <void> tag. The result of an expression in a <void> tag is still evaluated and used by any objects it encloses.

When an expression contains <void> tags (whether they denote expressions or statements) without class attributes, those <void> tags must follow all other tags in the expression. Each non-<void> expression is evaluated and the enclosing method is called with the results as arguments. The <void>-tagged statements and expressions are then applied, in order, to the result.

For example, consider the following expression:

<object class="javax.swing.JButton">
  <string>Press me</string>
  <void method="setName">
    <string>Greeting</string>
  </void>
</object>

It could be written as follows:
JButton button1 = new JButton("Press me");
button1.setName("Greeting");

The ability to nest expressions and statements greatly reduces the number of identifiers that are needed to represent a given graph.


Identifiers

When a graph contains cycles or multiple references to the same object, a name (an identifier) must be given to the object so that it can be referred to later. Identifiers are created using the id attribute, which binds a name to the expression value. The identifier has global scope extending from the last argument of the expression to the end of the file. In a streamed environment, the scope extends until the stream is flushed.

The following expression creates an identifier button1, bound to an instance of the JButton class:

<object id="button1" class="javax.swing.JButton"/>
Reference is made to named instances by using an idref attribute in an element with the <object> tag. The expression defining the identifier must precede any reference made to that identifier. The following expression makes reference to a previously defined instance button1:
<object idref="button1"/>
The expression
<object class="javax.swing.JPanel">
  <void method="add">
    <object id="button1" class="javax.swing.JButton"/>
  </void>
  <void method="add">
    <object class="javax.swing.JLabel">
      <void method="setLabelFor">
        <object idref="button1"/>
      </void>
    </object>
  </void>
</object>

corresponds to the following Java code:
JPanel panel1 = new JPanel();
JButton button1 = new JButton();
JLabel label1 = new JLabel();
panel1.add(button1);
panel1.add(label1);
label1.setLabelFor(button1);

The id attribute can be used with a <void> tag when the tag denotes an expression, rather than a statement. This allows you to make a reference to the result of an expression without contributing to the arguments of the enclosing method.

For example, consider the following fragment:

<object class="java.util.Date">
  <void id="now" method="getTime"/>
</object>
It allows an expression to be evaluated in the context of the enclosing instance, in this case defining the variable now as the value of the expression. It corresponds to the following Java code:
long now = new Date().getTime();

Abbreviations

The preceding information is all you need to be able to write XML archives readable by XMLDecoder. To read all archives produced by XMLEncoder, however, you need to know about the abbreviations for primitives, null, Class objects, static constants, properties, indexes, and arrays.


Primitives

The eight primitive data types of the Java platform can be used interchangeably with their corresponding wrapper classes in archives. Abbreviations exist for all the wrapper classes, as elements whose tag name is the name of the primitive type and whose body is the string representation of the value, as specified by the toString method of the wrapper class. Except for char, the body of the expression is supplied to the constructor of the corresponding wrapper that takes a single, String argument. The char type is handled as a special case since no string constructor exists in its wrapper class, Character.

The following tags represent both the primitive types and their corresponding wrapper classes:

  • <boolean>
  • <byte>
  • <char>
  • <short>
  • <int>
  • <long>
  • <float>
  • <double>
For example, the expression
<object class="java.lang.Integer">
  <string>123</string>
</object>
is shortened to
<int>123</int>
which might represent either of the following Java code fragments:
new Integer("123")        
123

Null

To specify null, use the <null> tag. A null element has no attributes and contains no other tags. For example:
<null/>

Class Objects

The <class> tag can be used to represent an instance of Class. For example,
<object class="java.lang.Class method="forName">
   <string>java.awt.event.ActionListener</string>
</object>
is shortened to
<class>java.awt.event.ActionListener</class>

which is equivalent to ActionListener.class.


Static Constants
(only in releases after 1.4.0 beta)

As of the release following 1.4.0 beta, the values of static constants may be written using the class and field attributes to specify the declaring class and field name of the constant, respectively. Thus
<void class="javax.swing.JTable" method="getField"> 
  <string>AUTO_RESIZE_OFF</string> 
  <void id="Integer0" method="get"> 
    <null/> 
  </void> 
</void> 
<object idref="Integer0"/> 

is shortened to
<object class="javax.swing.JTable" field="AUTO_RESIZE_OFF"/> 

which represents JTable.AUTO_RESIZE_OFF.


Properties

Method names beginning with get and set can be written using the property attribute instead of the method attribute.

For expressions with methods whose names begin with "get", the property name is the method name with "get" removed and the next letter made lowercase. Thus

<void method="getText"/>
is shortened to:
<void property="text"/>

For statements with methods whose names begin with "set", the property name is derived in a similar way. Thus

<void method="setText">
  <string>Hello, world</string>
</void>
is shortened to:
<void property="text">
  <string>Hello, world</string>
</void>

Indexes

Methods named exactly get or set, as defined in the java.util.List interface, can be written using the index attribute instead of the method attribute.

For expressions with the method name get, the value of the index attribute is used as the argument. Thus

<void method="get">
  <int>3</int>
<void>
is shortened to
<void index="3"/>
which corresponds to the following Java code:
Object o = aList.get(3);

For statements with the method name set, the value of the index attribute is prepended to the arguments of the enclosed body. Thus

<void index="3">
  <string>Hello, world</string>
</void>
is equivalent to
<void method="set">
  <int>3</int>
  <string>Hello, world</string>
</void>
which corresponds to the following Java code:
aList.set(3, "Hello, world")

Arrays

The <array> tag can be used to represent arrays. The class and length attributes denote the subtype and the length of the array respectively. You can use an id attribute to name the array. Here is an example of using an <array> tag:
<array class="java.awt.Component" length="3"/>
It corresponds to the following Java code:
Component[] a = new Component[3];
The set and get methods, as defined in the java.util.List interface, can be used as if they could be applied to array instances. The index attribute can thus be used with arrays.

Thus the expression

<array class="java.lang.String" length="3">
  <void index="1">
    <string>Hello, world</string>
  </void>
</array>
is equivalent to the following:
String[] s = new String[3];
s[1] = "Hello, world";

After the 1.4.0 beta release, you can omit the length attribute from an <array> tag and specify the values of entries directly, without using void tags. The length of the array is equal to the number of values specified. For example,

<array class="int">
  <int>123</int>
  <int>456</int>
</array>
represents the following Java code fragment:
int[] intArray = {123, 456};

The Top Level

Each element that appears in the body of the outermost (<java>) element is evaluated in the context of the decoder itself. Typically this outer context is used to retrieve the owner of the decoder, which can be set before reading the archive. (See the API documentation of XMLDecoder and XMLEncoder for details of the setOwner and getOwner methods.) The owner is a property of the decoder and can be accessed in the usual way:
<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
  <void id="myController" property="owner"/>
  ...objects go here...
</java>

The myController identifier can then by used throughout the body of the document to refer to the owner of the decoder. The following XML code creates a button that calls a no-argument doIt method on the owner when the button is pressed:

<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
  <void id="myController" property="owner"/>
  <object class="javax.swing.JButton">
    <void method="addActionListener">
      <object class="java.beans.EventHandler" method="create">
        <class>java.awt.event.ActionListener</class>
        <object idref="myController"/>
        <string>doIt</string>
      </object>
    </void>
  </object>
</java>

It is also possible to use the top-level environment to produce side effects on the owner. Typically this is used to set property values on the owner to supply it with references to parts of a user interface so that the owner can manipulate the UI programatically. The following XML code creates a button and assigns it to the quitButton property of the owner by calling the setQuitButton method on the owner when the file is read.

<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
  <void property="owner">
    <void property="quitButton">
       <object class="javax.swing.JButton"/>
    </void>
  </void>
</java>


DTD

This document described both the syntax and semantics of the XML schema used by XMLEncoder for saving archives of beans. A DTD that describes the syntax of the schema is in the file javabeans.dtd.

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.