Friday, August 04, 2006
Basic Castor XML Example
Castor XML is an open-source framework that simplifies the task of marshalling Java objects to and from XML documents. I chose Castor over JAXB because after a little research it sounded like Castor was easier to configure and that it would be better at handling XML documents for which I do not have an XML Schema.
In this article I will show how to use Castor to create an XML document from some Java objects. Then I'll demonstrate going in the other direction - reading an XML document and populating some Java objects.
First, create the Java classes. For this example I'll create a simple User class with an Address class. Note that each of the classes implements Serializable and has a default constructor. Each of them also adheres to the JavaBeans naming conventions. This isn't a requirement but if you don't then you must provide a mapping file.
Then create a class that populates the objects and calls Castor to dump them to XML.
Next, find all of the required libraries below, place them in the CLASSPATH, and compile the classes. The exact versions that I used are not required.
castor-1.0.1.jar
log4j-1.2.13.jar
commons-logging-1.1.jar
xercesImpl.jar (Xerces 2.8)
Finally, create a castor.properties file containing the following line and place it in the CLASSPATH. This setting causes Castor to indent the XML file so it is easier to read. In a production environment you probably want to turn indentation off so you're not moving around unnecessary whitespace.
When you run the example you should get a document that looks like the following:
There are some interesting things to note about the XML:
Now with just a few more lines of code I can read the document back into Java
objects.
That's all there is to it! In future blogs I'll show how Castor handles collections and then how its mapping file can be used to configure Castor to read and write XML documents that are not in its default format.
In this article I will show how to use Castor to create an XML document from some Java objects. Then I'll demonstrate going in the other direction - reading an XML document and populating some Java objects.
First, create the Java classes. For this example I'll create a simple User class with an Address class. Note that each of the classes implements Serializable and has a default constructor. Each of them also adheres to the JavaBeans naming conventions. This isn't a requirement but if you don't then you must provide a mapping file.
// from User.java
public class User implements java.io.Serializable {
private String firstName;
private String lastName;
private java.util.Date birthDate;
private int numAnnoyingHabits;
private Address address;
public User() {
}
public Address getAddress() {
return address;
}
public void setAddress(Address a) {
address = a;
}
public java.util.Date getBirthDate() {
return birthDate;
}
public void setBirthDate(java.util.Date d) {
birthDate = d;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String name) {
firstName = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String name) {
lastName = name;
}
public int getNumAnnoyingHabits() {
return numAnnoyingHabits;
}
public void setNumAnnoyingHabits(int i) {
numAnnoyingHabits = i;
}
}
// from Address.java
public class Address implements java.io.Serializable {
private String address;
private String city;
private String state;
private boolean isPrimary;
private int numberOfYearsActive;
public Address() { }
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public boolean isPrimary() {
return isPrimary;
}
public void setPrimary(boolean isPrimary) {
this.isPrimary = isPrimary;
}
public int getNumberOfYearsActive() {
return numberOfYearsActive;
}
public void setNumberOfYearsActive(int numberOfYearsActive) {
this.numberOfYearsActive = numberOfYearsActive;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Then create a class that populates the objects and calls Castor to dump them to XML.
// from Main.java
import java.io.*;
import org.exolab.castor.xml.*;
public class Main {
public static void main(String [] args) {
// first populate objects
User user = new User();
user.setFirstName("Afirst");
user.setLastName("Alast");
user.setBirthDate(new java.util.Date());
user.setNumAnnoyingHabits(2);
Address addr = new Address();
addr.setAddress("100 A Blvd");
addr.setCity("Peculiar");
addr.setState("MO");
addr.setPrimary(true);
addr.setNumberOfYearsActive(4);
user.setAddress(addr);
// now call Castor to write them to XML
try {
Writer writer = new FileWriter("user.xml");
Marshaller.marshal(user, writer);
writer.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Next, find all of the required libraries below, place them in the CLASSPATH, and compile the classes. The exact versions that I used are not required.
castor-1.0.1.jar
log4j-1.2.13.jar
commons-logging-1.1.jar
xercesImpl.jar (Xerces 2.8)
Finally, create a castor.properties file containing the following line and place it in the CLASSPATH. This setting causes Castor to indent the XML file so it is easier to read. In a production environment you probably want to turn indentation off so you're not moving around unnecessary whitespace.
org.exolab.castor.indent=true
When you run the example you should get a document that looks like the following:
<?xml version="1.0" encoding="UTF-8"?>
<user num-annoying-habits="2">
<birth-date>2006-08-05T21:42:46.355-05:00</birth-date>
<address number-of-years-active="4" primary="true">
<address>100 A Blvd</address>
<state>MO</state>
<city>Peculiar</city>
</address>
<last-name>Alast</last-name>
<first-name>Afirst</first-name>
</user>
There are some interesting things to note about the XML:
- First off, it worked! I just serialized some Java objects to XML with almost no effort!
- By default, primitive types are written as attributes.
- The default Date format is less than friendly. Unfortunately you must create a custom field handler to use another format. Hopefully I'll find time to demonstrate that in the future.
Now with just a few more lines of code I can read the document back into Java
objects.
// add to Main.java after marshalling code
// unmarshall file to a new object
Reader reader = new FileReader("user.xml");
User user2 = (User) Unmarshaller.unmarshal(User.class, reader);
That's all there is to it! In future blogs I'll show how Castor handles collections and then how its mapping file can be used to configure Castor to read and write XML documents that are not in its default format.