Serializing Java Objects with XStream
August 18, 2004
Joe Walnes's XStream is a unique open-source Java library for serializing objects into XML and deserializing that XML into objects. Unlike other APIs, such as David Megginson's XMLWriter or Elliotte Rusty Harold's XOM, which use specific classes and methods to produce XML, XStream relies on Java idioms such as object names to produce element names and strings within classes to produce element content. It also produces a kind of reflection of objects in XML.
XStream is being actively developed by a small project team with over a dozen contributors. It promises to become a very useful tool for persistence and transport; and I think it embraces, as does Joe Walnes, some virtues of agile programming.
Joe Walnes explained the following in private correspondence:
XStream's primary purpose is for serialization. It allows existing Java objects to be converted to clean XML and then restored again, without modifications. This is particularly useful as a form of persistence (no complicated database mappings required) or for sending objects over the wire. It has been optimized for this and is in heavy use on production systems.
XStream offers a two-minute tutorial and JavaDocs. Unfortunately, the XStream distribution doesn't yet provide example programs, though there is a test suite. This article demonstrates several example Java programs that should help you get started with XStream today.
The current, stable version of XStream is 1.0.1, which
was released at the end of May 2004. This article, however, uses the latest snapshot
available in late July 2004. Programs were compiled and run with Sun's Java 1.4.2_05 and included an
XStream JAR (xstream-SNAPSHOT.jar
) in the classpath at compile time. The
examples also use Aleksander Slominski's speedy XML Pull Parser or XPP
(xpp3-1.1.3.3_min.jar
) at runtime. Both the stable and snapshot versions of
XStream includes JARs. Download both the XStream archive and the example programs
to a
working directory.
A Simple Program
Let's start with a simple program that shows the basics of XStream serialization.
The
program Hello.java
follows. It is available in the sample archive for this article. The program
uses XStream to output a string in XML:
import com.thoughtworks.xstream.XStream; public class Hello { public static void main(String[] args) { XStream xstream = new XStream(); String salutation = "Hello, World!"; String xml = xstream.toXML(salutation); System.out.print(xml); } }
The public class Hello
imports only one class,
com.thoughtworks.xstream.XStream
. It calls a constructor for XStream,
creating the object xstream
. Then it uses the toXML
method to
store the string salutation
as XML, and writes the XML to the console using the
System.out.print
method.
Compile the program using this command line at a shell prompt in the working directory:
javac -classpath .;xstream-SNAPSHOT.jar Hello.java
This shows the current directory and the XStream JAR in the classpath explicitly.
(Of
course, use colons instead of semicolons if you are on Unix.) Once Hello.java
compiles successfully, run it with this line:
java -cp .;xstream-SNAPSHOT.jar;xpp3-1.1.3.3_min.jar Hello
This line adds the XPP JAR to the command line; it should output the following:
<string>Hello, World!</string>
XStream automatically wraps the Java String salutation
in the XML element
string
. The string
element appears to be an example of XStream's
style of XML reflection. If you really want to see this in action, try
Reflect.java
:
import com.thoughtworks.xstream.XStream; public class Reflect { public static void main(String[] args) { XStream xstream = new XStream(); String xml = xstream.toXML(xstream); System.out.print(xml); } }
This program serializes the object xstream
into XML, producing over 7,000
lines of "reflection." If you want to avoid the dependency on XPP, you can use DOM
instead, like this (HelloDom.java
):
import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; public class HelloDom { public static void main(String[] args) { XStream xstream = new XStream(new DomDriver()); String salutation = "Hello, World!"; String xml = xstream.toXML(salutation); System.out.print(xml); } }
The differences between Hello.java
and HelloDom.java
are
highlighted in bold: HelloDom.java
imports the additional class
com.thoughtworks.xstream.io.xml.DomDriver
and then uses
DomDriver
in the XStream constructor. When you run HelloDom
, you
can drop the reference to the XPP JAR on your classpath.
Using Another Class
The next program, Instant.java
, uses a non-public outer class,
Date
, to represent some data for a date:
import com.thoughtworks.xstream.XStream; class Date { int year; int month; int day; } public class Instant { public static void main(String[] args) { XStream xstream = new XStream(); Date date = new Date(); date.year = 2004; date.month = 8; date.day = 15; xstream.alias("date", Date.class); String decl = "<?xml version=\"1.0\"?>\n"; String xml = xstream.toXML(date); System.out.print(decl + xml); } }
The class Date
holds three fields, all of which are integers. After the
Date
constructor, each of these fields is given a value, e.g. date.year
= 2004;
. The alias
method creates an alias XML element name for the
Date
class, changing the default name from Date
to
date
. XStream uses the fully qualified class name for the element name,
including the package name, so the alias
method will come in handy for tweaking
names. The program also creates an XML declaration and uses it in its output.
Compile and run the program, and you will get this output:
<xml version="1.0"?> <date> <year>2004<year> <month>8<month> <day>15<day> <date>
Deserializing XML to Objects
The final program, Deserialize.java
, shows you how to deserialize XML to an
object, and then to reuse it:
import com.thoughtworks.xstream.XStream; class Date { int year = 2004; int month = 8; int day = 15; } public class Deserialize { public static void main(String[] args) { XStream xstream = new XStream(); Date date = new Date(); xstream.alias("date", Date.class); String xml = xstream.toXML(date); System.out.print(xml); Date newdate = (Date)xstream.fromXML(xml); newdate.month = 12; newdate.day = 2; String newxml = xstream.toXML(newdate); System.out.print("\n\n" + newxml); } }
Note this line from Deserialize.java
:
Date newdate = (Date)xstream.fromXML(xml);
The line creates a new object newdate
using the fromXML
method to
convert the string xml
back to an object (also casting it as
Date
).
After compiling and running Deserialize.java
, you will see this output (two
XML documents):
<date> <year>2004<year> <month>8<month> <day>15<day> <date> <date> <year>2004<year> <month>12<month> <day>2<day> <date>
The second instance of date
comes from the object created from deserialized
XML.
Conclusion
This article has introduced you to the basic features and capabilities of XStream. For additional insights, you might have a look at XStream test programs that use maps or collections (from the test suite). There is plenty of CVS activity, enough to give me hope that XStream is in active development and will be around for awhile. I think XStream takes a nifty approach to XML serialization and persistence, and I'll be keeping my eye on it for even more innovation in the future.