Monday, August 9, 2010

How to load/write XML using JAXB?

Every now and then you need to load or write some XML files. It is very effective way to put data in machine-readable form. XML is text format file which is well formatted using standardized rules.

So to demonstrate use of JAXB we will:
1) create XML file with some data (bookmarks.xml)
2) create XML schema file to validate our XML file (bookmarks.xsd)
3) create XML object model using xjc utility (xjc.txt)
4) load that XML file into JAXB object (XMLUnmarshall.java)
5) write JAXB object back over original XML file (XMLMarshaller.java)
6) create main java class to call these functions in appropriate order (JAXBLoader,java)

Create XML file
Lets create a simple xml file for storing bookmark data organized through sets. It will contain root element bookmarks with bookmark elements inside and list of attribute elements for storing href values. Also for every bookmark element there will be a attribute set which will identify current bookmark set. Example of this xml can be found here: bookmarks.xml.

Create XML schema file
To validate bookmark.xml we need to create schema file. Example for this example can be found here: bookmarks.xsd.

Create object model
To create object model for our xml we need to run xjc utility and supply it with xsd schema file as argument at command line.

zlaja@orion:~/NetBeansProjects/JAXBLoader> xjc ./data/bookmarks.xsd -p com.blogspot.zetaorionis.bookmarks.model -d ./src
parsing a schema...
compiling a schema...
com/blogspot/zetaorionis/bookmarks/model/AttributeType.java
com/blogspot/zetaorionis/bookmarks/model/Bookmark.java
com/blogspot/zetaorionis/bookmarks/model/Bookmarks.java
com/blogspot/zetaorionis/bookmarks/model/ObjectFactory.java
com/blogspot/zetaorionis/bookmarks/model/package-info.java
zlaja@orion:~/NetBeansProjects/JAXBLoader>

Unmarshalling / loading XML data to object model instance
This utility class will help you read xml file into it's object representation. It will work with every xml file. You just need to supply it with uri path to xml file:
data/bookmarks.xml

and root element class of object model:
Bookmarks.class

Here is XMLUnmarshaller.java utility class:

package com.blogspot.zetaorionis.util.xml;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.net.URI;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;

/**
*
* @author zlaja
*/
public class XMLUnmarshaller {

private static final Logger logger = Logger.getLogger(XMLUnmarshaller.class);
/**
* The bookmark file.
*/
protected URI uri;

/**
* Create an xml based loader of bookmarks.
* @param uri the bookmark file
*/
public XMLUnmarshaller(final URI uri) {
this.uri = uri;
}

//
// BookmarksXMLLoader
//
/**
* Load bookmarks into a action list.
*
* @throws IOException
* If an I/O error occurred.
* @throws FileNotFoundException
* If the resource was not found.
*/
public T load(Class docClass) throws IOException {
final InputStream in = getClass().getResourceAsStream("/" + uri.getPath());

if (in == null) {
throw new FileNotFoundException("Cannot find resource: " + uri);
}

try {
return load(docClass, in);
} finally {
in.close();
}
}

protected T load(Class docClass, final InputStream in) {
T o = null;
try {
o = unmarshal(docClass, in);
} catch (JAXBException ex) {
logger.error("Error while unmarshalling.", ex);
}
return o;
}

public T unmarshal(Class docClass, InputStream inputStream)
throws JAXBException {
String packageName = docClass.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance(packageName);
Unmarshaller u = jc.createUnmarshaller();
JAXBElement doc = u.unmarshal(new StreamSource(inputStream), docClass);
return doc.getValue();
}
}

Marshalling / writing data to xml from object model instance
This utility class will help you write data from object representation to xml file. It will work with every xml file. You just need to supply it with uri path to xml file:
data/bookmarks.xml

and root element class of object model:
Bookmarks.class

Here is XMLMarshaller.java utility class:
package com.blogspot.zetaorionis.util.xml;

import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.net.URI;
import java.io.IOException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.apache.log4j.Logger;

/**
*
* @author zlaja
*/
public class XMLMarshaller {

private static final Logger logger = Logger.getLogger(XMLUnmarshaller.class);
/**
* The output XML file.
*/
protected URI uri;

/**
* Create an xml based writer for specified jaxbObject.
* @param uri - uri for output XML file
*/
public XMLMarshaller(final URI uri) {
this.uri = uri;
}

//
// XMLMarshaller
//
/**
* Write JAXBElement representation of object to XML file.
*
* @param jaxbObject - object for marshalling to xml, converted to JAXBElement.
* Conversion is done using function inside ObjectFactory.java which is
* created with xjc utility
* @param docClass - class for object that is going to be marshalled to XML
*
* @throws IOException
* If an I/O error occurred.
* @throws FileNotFoundException
* If the resource was not found.
*/
public void write(final JAXBElement jaxbObject, Class docClass) throws IOException {
final OutputStream os = new FileOutputStream(uri.getPath());

if (os == null) {
throw new FileNotFoundException("Cannot create resource: " + uri);
}

try {
write(jaxbObject, docClass, os);
} finally {
os.close();
}
}

protected void write(final JAXBElement jaxbObject, Class docClass, final OutputStream os) {
try {
marshall(jaxbObject, docClass, os);
} catch (JAXBException ex) {
logger.error("Error in marshalling to XML.", ex);
}
}

private void marshall(final JAXBElement jaxbObject, Class docClass, final OutputStream os)
throws JAXBException {
String packageName = docClass.getPackage().getName();
JAXBContext context = JAXBContext.newInstance(packageName);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(jaxbObject, os);
}
}

Call utility classes inside main class
package com.blogspot.zetaorionis.jaxbloader;

import com.blogspot.zetaorionis.bookmarks.model.Bookmarks;
import com.blogspot.zetaorionis.bookmarks.model.ObjectFactory;
import com.blogspot.zetaorionis.util.xml.XMLMarshaller;
import com.blogspot.zetaorionis.util.xml.XMLUnmarshaller;
import java.net.URI;
import org.apache.log4j.Logger;

/**
*
* @author zlaja
*/
public class JAXBLoader {

private static final String LOG4J_PROPERTIES = "data/log4j.properties";
private static final Logger logger = Logger.getLogger(JAXBLoader.class);
private Bookmarks bookmarks = new Bookmarks();

public JAXBLoader() {
Log4J.init(LOG4J_PROPERTIES);
}

/**
* Loads XML data to object
*/
private void loadBookmarks() {
try {
final String path = "data/bookmarks.xml";
final URI uri = new URI(path);

final XMLUnmarshaller xmlBookmarks = new XMLUnmarshaller(uri);
this.bookmarks = xmlBookmarks.load(Bookmarks.class);
logger.info("Info: Bookmarks loaded successfuly.");
} catch (Exception ex) {
logger.error("Error: Loading bookmarks XML file failed", ex);
}
}

/**
* Writes object data to XML
*/
private void writeBookmarks() {
try {
final String path = "data/bookmarks.xml";
final URI uri = new URI(path);

final XMLMarshaller xmlBookmarks = new XMLMarshaller(uri);
ObjectFactory of = new ObjectFactory();
xmlBookmarks.write(of.createBookmarks(this.bookmarks), Bookmarks.class);
logger.info("Info: Bookmarks written successfuly.");
} catch (Exception ex) {
logger.error("Error: Writing bookmarks XML file failed", ex);
}
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
JAXBLoader loader = new JAXBLoader();
loader.loadBookmarks();
loader.writeBookmarks();
}
}

No comments: