Friday, March 2, 2012

XML - Castor Parser

Castor is an open source XML parser, similar to JAXB (which is a Java Standard).

Castor allows you to define your own mapping - you can tell Castor exactly how you want to extract the data to Java objects. This way we can create Java objects more suitable to our needs, rather than auto-generated classes which might not correctly represent our "business" model.

A business model is a set of objects which represent the system being modeled(programmed). For example, if we are modeling a book store, a class Shelf.java and Book.java better represent the business model, then say BookData.java and Entry.java - which might get auto generated if the XML had elements like <bookdata> and <entry> respectively.

Let us parse the users.xml file with Castor.

First, update your ivy.xml to download Castor (and any dependent jars).
My ivy.xml now looks like this:
 <ivy-module version="2.0">  
<info organisation="org.confucius" module="helloworld"/>
<dependencies>
<dependency org="commons-lang" name="commons-lang" rev="2.6"/>
<dependency org="org.codehaus.castor" name="castor-core" rev="1.3.2"/>
<dependency org="org.codehaus.castor" name="castor-xml" rev="1.3.2"/>
<dependency org="org.springframework" name="spring-webmvc" rev="2.5.6"/>
<dependency org="org.springframework" name="spring-web" rev="2.5.6"/>
<dependency org="org.springframework" name="spring" rev="2.5.6"/>
<dependency org="commons-pool" name="commons-pool" rev="20030825.183949"/>
<dependency org="commons-dbcp" name="commons-dbcp" rev="20030825.184428"/>
<dependency org="antlr" name="antlr" rev="20030911"/>
<dependency org="javassist" name="javassist" rev="3.12.1.GA"/>
<dependency org="mysql" name="mysql-connector-java" rev="5.1.18"/>
<dependency org="javax.persistence" name="persistence-api" rev="1.0.2"/>
<dependency org="jboss" name="jboss-j2ee" rev="4.2.2.GA"/>
<dependency org="org.jboss.logging" name="jboss-logging" rev="3.1.0.GA"/>
<dependency org="org.slf4j" name="slf4j-simple" rev="1.6.1"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.4"/>
<dependency org="org.hibernate" name="hibernate3" rev="3.6.0.Final"/>
<dependency org="dom4j" name="dom4j" rev="1.6.1"/>
<dependency org="struts" name="struts" rev="1.2.9"/>
<dependency org="org.apache.struts" name="struts-taglib" rev="1.3.10"/>
<dependency org="commons-collections" name="commons-collections" rev="20040616"/>
<dependency org="commons-digester" name="commons-digester" rev="2.1"/>
<dependency org="commons-beanutils" name="commons-beanutils" rev="20030211.134440"/>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"/>
<dependency org="org.apache.httpcomponents" name="httpcore" rev="4.2-alpha2"/>
<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.2-alpha1"/>
<dependency org="org.apache.commons" name="commons-exec" rev="1.1"/>
<dependency org="com.google.guava" name="guava" rev="r09"/>
<dependency org="org.seleniumhq.selenium" name="selenium-api" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-remote-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-firefox-driver" rev="2.17.0"/>
<dependency org="org.seleniumhq.selenium" name="selenium-java" rev="2.16.1"/>
<dependency org="junit" name="junit" rev="4.10"/>
<dependency org="org.json" name="json" rev="20090211"/>
<dependency org="javax.servlet" name="servlet-api" rev="2.5"/>
<dependency org="javax.servlet" name="jsp-api" rev="2.0"/>
<dependency org="jstl" name="jstl" rev="1.2"/>
<dependency org="log4j" name="log4j" rev="1.2.16"/>
</dependencies>
</ivy-module>

Run the Ant 'resolve' task to download the castor jars to /lib.
In Eclipse, go to Project-->Properties-->Java Build Path-->Libraries-->Add Jars and add the castor jars.

Now we will create the Java classes which we want the XML to be parsed to.

In your /src/org/confucius, create a class SwimmingPool.java, like this:
 package org.confucius;  

import java.util.List;

public class SwimmingPool {
private List<User> userList;

public List<User> getUserList() {
return userList;
}

public void setUserList(List<User> userList) {
this.userList = userList;
}
}

This contains a List of User objects.
The User.java class is the same one we created in /src/org/confucius in the previous post, and it looks like this:
 package org.confucius;   


public class User {
private String firstName;
private String lastName;

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getFirstName() {
return firstName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getLastName() {
return lastName;
}

}

Now create a file castor-mapping.xml in your /data folder, like this:
 <?xml version="1.0"?>  
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="org.confucius.SwimmingPool">
<map-to xml="users"/>

<field name="userList" type="org.confucius.User" collection="vector">
<bind-xml name="user" />
</field>
</class>

<class name="org.confucius.User">
<field name="firstName" type="string" />
<field name="lastName" type="string" />
</class>
</mapping>

This mapping file maps the <users> tag to the SwimmingPool class, and the <user> tag to the User class.

This is all that Castor needs to know to parse the users.xml file.

In your /src/org/confucius, create a class TestCastorParser.java, like this:
 package org.confucius;  

import java.io.FileReader;
import java.util.Iterator;

import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.xml.Unmarshaller;
import org.xml.sax.InputSource;

public class TestCastorParser {
public static void main (String[] args){

try {
Mapping mapping = new Mapping();
mapping.loadMapping("data/castor-mapping.xml");

Unmarshaller unmarshaller = new Unmarshaller(mapping);
SwimmingPool swimmingPool = (SwimmingPool)unmarshaller.unmarshal(new InputSource(new FileReader("data/users.xml")));

for (Iterator<User> iter = swimmingPool.getUserList().iterator(); iter.hasNext();){
User user = iter.next();
System.out.println("Got user: " + user.getFirstName() + " " + user.getLastName());
}
} catch (Exception e) {
e.printStackTrace();
}

}
}

We load the mapping file and then ask Castor to parse the users.xml file using the mapping.

R-click on the TestCastorParser.java file in Eclipse->Navigator view and select Run As->Java Application

You will see the Users printed to the console.