Update: Solution presented in this post is integrated in XStream 1.2.2 and you are advised to use the official (and maintained) release. See this post for more information.
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.
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.



Try using JsOrb. It has support for both XML and JS streaming, and allows direct access to Java interfaces from JavaScript, using normal Java semantics. It generates JS classes for your POJOs and proxies for your remote interfaces. A few tags in your JSP is all you need to get started.
One thing I've been wondering is why everyone talks so much about JSON as opposed to simply JavaScript? JSON is a subset of JavaScript...but you can just as easily send JavaScript across the wire and have that evaluated at the other end.
And sending JavaScript has one huge advantage: you can send object graphs with circular references. For example if you have a Product object (as in your example) that has a Pricing object attached to which also has a back reference to the Product, there is no way to construct this in JSON. But with JavaScript it's doable, and not that hard to automate.
This is the approach that Stripes takes. It has the ability to take an object graph and convert it to a series of JavaScript statements (some using JSON) that will reconstitute the whole graph on the browser. It handles circular references cleanly and you can also prune the graph by type (Class) to stop it going too far.
So, back to my initial question. Since all browsers that support JSON support JavaScript and the eval() method which can eval arbitrary JavaScript...what's the advantage of JSON as a data interchange format?
A web service is often useful to more than just an end user with a browser. It's also usable by other web services, both inside the organization itself and by outside organizations. JSON is a compact data format for them as well.
Hiya, I'm the Jettison author - sorry to hear that you had problems with it! Would love to integrate your patches though!! Please contact the mailing list or file a JIRA issue so we can get them in for our next release. Thanks!
Hi Dan,
I wouldn't call them problems, rather minor issues and enhancements. I plan to sort out changes that I've made, make some test cases and submit it all to Jira. I hope I'll have it all finished during next week. Thanks for the great library ...
When i tried using this approach i got java.lang.UnsupportedClassVersionError: org/sensatic/jqr/jason/JettisonDriver . I just got the snapshot jar
Hi Mike,
you are probably trying to run this on JDK 1.4 (or earlier) ... Some of these features are available from JDK 5 on ... Also, the code in the JAR is built on JDK 5. If you wish to use it in some older environment, try building the source.
Thanks,
Dejan
I downloaded jqr-json-1.0-SNAPSHOT.jar and found a problem. I was trying to serialized the Query class from Lucene and serialization broke with json while it is good with xml:
Query q=QueryParser.parse("\"test me\"","contents",new StandardAnalyzer());
XStream xstream = new XStream(new JettisonDriver());
String jsonResult=xstream.toXML(q);
System.out.println(jsonResult);
trying to serialize back the object causes a classcastexception. This only happens when you try parse a phrase query or a query with asian characters.
Hi John,
since some of my patches were applied to Jettison since this jar was built, I'm planning modify the driver to reflect those changes. I'll see to deal with your problem at the same time. I hope I'll find time to do this during the current week.
Regards,
Dejan
Hi Dejan:
Any updates with the problem I posted last month?
-John
John, unfortunately I didn't find time to deal with it. I think it's best to post the problem in Jettison's Jira. I've provided patches that I had to them, so I'm looking to modify this driver to work with official Jettison release (when I find time to do that).
When can I use Java and JSON. In which kind of situations?
I ran into an issue where Jettison represents all numeric values as strings, I wrote up a quick patch to the JettisonMappedXmlDriver. I tried posting on the codehaus Xstream and Jettison mailing lists but for some reason no matter what I do I can't subscribe any email addresses to those lists.
See the problem description:
JSON JettisonMappedXmlDriver quoting numeric values problem
Solution:
JSON JettisonMappedXmlDriver quoting numeric values Solution
JettisonMappedXmlDriver patch:
JettisonMappedXmlDriver.java
Hi Doug,
it would be the best if you could file a issue in Jettison Jira (http://jira.codehaus.org/browse/JETTISON) along with the patch and test cases. In any case, I'll take a look at this issue in the next couple of days.
BTW. We're working on making 1.0 RC3 available soon, so hopefully this will make into this release.
Thanks
Dejan
I submitted a bug report and a patch with unit tests to both XStream and Jettison.
Jettison - JSON JettisonMappedXmlDriver quoting numeric values breaking JSON 2 Java deserialization Issue
XStream - JSON JettisonMappedXmlDriver quoting numeric values breaking JSON 2 Java deserialization Issue