Technical Article
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
<java>
<?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 <
and &
escape sequences.
Expressions and Statements
<object><void><object><void>method
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>
"Press me"JButton
new JButton("Press me");
new
<object class="javax.swing.JButton">
<string>Press me</string>
</object>
<object class="javax.swing.JButton">
<void method="setText">
<string>Hello, world</string>
</void>
</object>
JButton b = new JButton();
b.setText("Hello, world");
<void><void>
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>
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
identifierid
The following expression creates an identifier button1
, bound to an instance of the JButton
class:
<object id="button1" class="javax.swing.JButton"/>
idref<object>button1
<object idref="button1"/>
<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>
JPanel panel1 = new JPanel();
JButton button1 = new JButton();
JLabel label1 = new JLabel();
panel1.add(button1);
panel1.add(label1);
label1.setLabelFor(button1);
id<void>
For example, consider the following fragment:
<object class="java.util.Date">
<void id="now" method="getTime"/>
</object>
now
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
toStringcharStringcharCharacter
The following tags represent both the primitive types and their corresponding wrapper classes:
<boolean>
<byte>
<char>
<short>
<int>
<long>
<float>
<double>
<object class="java.lang.Integer">
<string>123</string>
</object>
<int>123</int>
new Integer("123")
123
Null
null<null>null
<null/>
Class Objects
<class>Class
<object class="java.lang.Class method="forName">
<string>java.awt.event.ActionListener</string>
</object>
<class>java.awt.event.ActionListener</class>
which is equivalent to ActionListener.class
.
Static Constants (only in releases after 1.4.0 beta)
classfield
<void class="javax.swing.JTable" method="getField">
<string>AUTO_RESIZE_OFF</string>
<void id="Integer0" method="get">
<null/>
</void>
</void>
<object idref="Integer0"/>
<object class="javax.swing.JTable" field="AUTO_RESIZE_OFF"/>
which represents JTable.AUTO_RESIZE_OFF
.
Properties
getsetpropertymethod
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"/>
<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>
<void property="text">
<string>Hello, world</string>
</void>
Indexes
getset
java.util.List
indexmethod
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>
<void index="3"/>
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>
<void method="set">
<int>3</int>
<string>Hello, world</string>
</void>
aList.set(3, "Hello, world")
Arrays
<array>classlengthid<array>
<array class="java.awt.Component" length="3"/>
Component[] a = new Component[3];
setgetjava.util.Listindex
Thus the expression
<array class="java.lang.String" length="3">
<void index="1">
<string>Hello, world</string>
</void>
</array>
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>
int[] intArray = {123, 456};
The Top Level
<java>
owner
XMLDecoder
XMLEncoder
setOwner
getOwner
<?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
XMLEncoder javabeans.dtd