Java and JSON

It seems that JSON vs. XML debate was one of the hot topics for this winter. Again, I think that “vs.” part is sufficient and that both XML and JSON should have their place in overall technology landscape. One of the obvious JSON advantages is that it can be directly evaluated in JavaScript. And for the Ajax-world we live in, it is not a small thing.

For one project, I started looking for a library that would enable me easy transformations between Java and JSON data. JSON in Java looks nice, but what I really want is a more automated and configurable library like those we have for XML processing. My first thought was XStream since I like its simple API, extensible architecture, powerful converters and support for annotations. After first check I found that from version 1.2 on there is a partial support for JSON (which was logical thing to expect). So, currently you can serialize your Java objects to JSON format. Here’s a little example:

public class JSONWrite {

	public static void main(String[] args) {
		Product product = new Product("Banana", "123", 23.00);
		XStream xstream = new XStream(new JsonHierarchicalStreamDriver());
		String result = xstream.toXML(product);
		System.out.println(result);
	}

}

This example initiates a Product object (with name, id and price properties) and serialize it through JsonHierarchicalDriver. As a result we will get the following output:

{"org.sensatic.jqr.json.Product": {
  "name": "Banana",
  "id": "123",
  "price": {"23.0"}
}}

Unfortunately, there is no read support at this moment for JSON format. So if you try to convert this JSON data back to Java object

public class JSONRead {

	public static void main(String[] args) {
		String json = "{"org.sensatic.jqr.json.Product": {"
			  + ""name": "Banana","
			  + ""id": "123","
			  + ""price": {"23.0"}"
			 + "}}";
		XStream xstream = new XStream(new JsonHierarchicalStreamDriver());
		Product product = (Product)xstream.fromXML(json);
		System.out.println(product);
	}

}

you’ll get the following exception

java.lang.UnsupportedOperationException:
   The JsonHierarchicalStreamDriver can only write JSON

After a little more search, I stumbled upon Jettison project which implements “a collection of Stax parsers and writers which read and write JSON”. So instantly I wanted to create a XStream driver that uses Jettison as an underlying library to parse to and from JSON.

It was a little bit trickier then I first thought, since I had to patch Jettison (Mapped convention classes) to support nested arrays, element names with “.” (so it can use full class names as element names) and some other minor things. After all this, it worked at least for the usage that I needed at the moment (I didn’t test its behavior when used with namespaces and attributes).

So, if we now use JettisonDriver with the previous write example:

public class JSONWrite {

	public static void main(String[] args) {
		Product product = new Product("Banana", "123", 23.00);
		XStream xstream = new XStream(new JettisonDriver());
		String result = xstream.toXML(product);
		System.out.println(result);
	}

}

We’ll get the similar result:

{"org.sensatic.jqr.json.Product":{"name":"Banana","id":"123","price":"23.0"}}

But read operation now works as well. So the following code:

public class JSONRead {

	public static void main(String[] args) {
		String json = "{"org.sensatic.jqr.json.Product": {"
			  + ""name": "Banana","
			  + ""id": "123","
			  + ""price": "23.0""
			 + "}}";
		XStream xstream = new XStream(new JettisonDriver());
		Product product = (Product)xstream.fromXML(json);
		System.out.println(product);
	}

}

prints:

[Banana, 123, 23.0]

The real point of processing JSON with XStream is that now we can use all features already built-in for processing XML documents. For example we can add alias for the Product class. In order to do that we can put the following annotation in front of the Product class:

@XStreamAlias("product")

and modify our write example a bit:

public class JSONWrite {

	public static void main(String[] args) {
		Product product = new Product("Banana", "123", 23.00);
		XStream xstream = new XStream(new JettisonDriver());
		Annotations.configureAliases(xstream, Product.class);
		String result = xstream.toXML(product);
		System.out.println(result);
	}

}

and we’ll get a more compact JSON as a result:

{"product":{"name":"Banana","id":"123","price":"23.0"}}

You can grab the source of this driver from this:

svn co https://jqr.svn.sourceforge.net/svnroot/jqr/trunk/json json

Subversion repository. If you are interested only in binary version, you can download it from here:

http://surfnet.dl.sourceforge.net/sourceforge/jqr/jqr-json-1.0-SNAPSHOT.jar

For more complex examples you can look at unit tests. It also contains a driver based on BadgerFish Stax implementation (part of Jettison). It has the same problem with nested arrays and since I didn’t need it at the moment I left it as it is.

It currently includes patched Jettison source, but I’ll see to forward those changes where they belong and include Jettison as a dependency in the future. Frankly, this driver shouldn’t be a standalone code but integral part of some of those two projects. I’ll write here if anything changes about that, but if you need something like this today, you can take it from above locations.

Leave a comment

Your email address will not be published. Required fields are marked *