JSON Tutorial
Due to XStream's flexible architecture, handling of JSON mappings is as easy as handling of XML documents. All you have to do is to initialize XStream object with an appropriate driver and you are ready to serialize your objects to (and from) JSON.
XStream currently delivers two drivers for JSON: The JsonHierarchicalStreamDriver and the JettisonMappedXmlDriver. The first one does not have an additional dependency, but can only be used to write XML, while the second one is based on Jettison and can also deserialize JSON to Java objects again. However, since the JettisonMappedXmlDriver transforms plain XML into JSON, you might get better JSON strings with the JsonHierarchicalStreamDriver, because this driver knows often about the type of the written data and can act properly.
One word of warning. JSON is made for an easy data transfer between systems and languages. It's syntax offers much less possibilities to express certain data structures. It supports name/value pairs of primitive data types, arrays and lists and allows to nest them - but that's it! No references, no possibility for meta data (attributes), no properties with same names (as generated for implicit collections), etc. Therefore do not expect wonders. XStream (and Jettison) try their best, but the procedure to convert any kind of object into JSON is a lossy transformation and especially deserialization will not be possible for any construct. See also FAQ for JSON limitations. Also note that any number value in JavaScript is a 64-bit double precision float value according IEEE 754 standard. In consequence it is not possible to represent all values of a Java long type without loss of precision. See again FAQ for JSON and JavaScript.
Since JSON has no possibility to express references, you should always set the NO_REFERENCES mode writing JSON.
Jettison driver
Jettison driver uses Jettison StAX parser to read and write data in JSON
format. It is available in XStream since version 1.2.2 and is implemented in
com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver
class. To successfully use this driver you need
to have the Jettison project and StAX API in your classpath (see reference for
optional dependencies).
Alternatively you can download JARs manually.
Here are a few simple examples:
Write to JSON with the Jettison-based implementation
The following example:
package com.thoughtworks.xstream.json.test; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; public class WriteTest { public static void main(String[] args) { Product product = new Product("Banana", "123", 23.00); XStream xstream = new XStream(new JettisonMappedXmlDriver()); xstream.setMode(XStream.NO_REFERENCES); xstream.alias("product", Product.class); System.out.println(xstream.toXML(product)); } }
produces the following JSON document:
{"product":{"name":"Banana","id":123,"price":23.0}}
As you can see, all standard XStream features (such as aliases) can be used with this driver.
Note that Jettison will try to detect numerical values and omit the quotes. Since Jettison cannot know about the original data type, it has to guess. Hence it will therefore also write the value of the id field as numeric value in future.
Write to JSON with the self-contained JSON driver
The only difference to the example above is the line with the initialization:
XStream xstream = new XStream(new JsonHierarchicalStreamDriver());
The output is as follows:
{"product": { "name": "Banana", "id": "123", "price": 23.0 }}
While the difference because of line feeds is immediately obvious, you have to note also the value of the id element. This time the driver knew about the string value and therefore quotes were generated.
Write to JSON with the self-contained JSON driver dropping the root
Sometimes the root node in the generated JSON is superfluous, since its name is caused by the Java type of the written object that has no meaning in JSON and increases only the nesting level of the structure. Therefore it is possible to drop this root by initializing the internally used JsonWriter in a different mode. Again, the only difference to the example above is the line with the initialization:
XStream xstream = new XStream(new JsonHierarchicalStreamDriver() { public HierarchicalStreamWriter createWriter(Writer writer) { return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE); } });
The output is as follows:
{ "name": "Banana", "id": "123", "price": 23.0 }
Note, that it is now possible to create invalid JSON if XStream should marshal a single object with a single value to JSON (like String, int, URL, ...). JSON requires that the root is actually an object, but by dropping the root all that is generated is a single value. You can force the JsonWriter to throw a ConversionException in this case, see Javadoc of the JsonWriter.
Read from JSON
The following code:
package com.thoughtworks.xstream.json.test; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; public class ReadTest { public static void main(String[] args) { String json = "{\"product\":{\"name\":\"Banana\",\"id\":123" + ",\"price\":23.0}}"; XStream xstream = new XStream(new JettisonMappedXmlDriver()); xstream.alias("product", Product.class); Product product = (Product)xstream.fromXML(json); System.out.println(product.getName()); } }
serializes JSON document created with preceding example back to Java object. It prints:
Banana
as a result.