JSON support
zero.core package includes support for JavaScript Object Notation (JSON) data structures.
JSON is a simple data format that is commonly used to transfer data to JavaScript client code. More information about JSON can be found at the JSON Web site json.org.
JSON APIs
zero.json.Json is the primary IBM® WebSphere® sMash API for working with JSON, including parsing, serializing, and converting representations.
Common APIs:
public class Json {
/** Parse or decode a JSON encoded data from a String ... into a Java object*/
public static Object decode(String str) throws IOException { ... }
public static Object decode(InputStream is) throws IOException { ... }
public static Object decode(Reader reader) throws IOException { ... }
public static Object decode(URL url) throws IOException { ... }
/** Decoding JSON objects into Maps where the order of the elements are to be preserved */
public static Object decode(String str, boolean ordered) throws IOException { ... }
public static Object decode(InputStream is, boolean ordered) throws IOException { ... }
public static Object decode(Reader reader, boolean ordered) throws IOException { ... }
public static Object decode(URL url, boolean ordered) throws IOException { ... }
/** Decoding JSON objects/arrays into custom Java Objects */
public static Object decode(String str, JsonParserCallback cb) throws IOException { ... }
public static Object decode(InputStream is, JsonParserCallback cb) throws IOException { ... }
public static Object decode(Reader reader, JsonParserCallback cb) throws IOException { ... }
public static Object decode(URL url, JsonParserCallback cb) throws IOException { ... }
/** Serialize or encode a Java object as JSON data */
public static String encode(Object obj) throws IllegalArgumentException, ...{ ... }
public static void encode(Object obj, OutputStream os) throws IllegalArgumentException, { ... }
public static void encode(Object obj, Writer writer) throws IllegalArgumentException { ... }
/** Encode a Java object with formatting */
public static String encode(Object obj, boolean formatted) throws IllegalArgumentException, { ... }
public static void encode(Object obj, OutputStream os, boolean formatted) throws IllegalArgumentException, { ... }
public static void encode(Object obj, Writer writer, boolean formatted) throws IllegalArgumentException, { ... }
/** Convert a decoded object into a custom Java object */
public static <T> T toObject(Object jsonObj, Class<T> type) { ... }
}
decode() returns one of the following Java types:
-
java.lang.String -
java.lang.Number -
java.lang.Boolean -
java.util.Map, which represents JSON objects -
java.util.List, which represents JSON arrays -
null
decode() can also be used to eliminate any deep copies if the object to be deserialized into is not one of the above standard Java types. The callback interface will be used by the JSON parser to delegate creation and population of JSON objects and arrays to the callback implementation.
By default, conversions between Java objects and JSON operate upon only public properties of the Java objects. "Public properties" of a Java object are properties associated with either public getter/setter methods or public (non-final) fields. Default conversions attempt to use getter/setter methods first.
Custom converters may be incorporated, as noted below.
PHP example
The following example illustrates how to parse JSON data that has been sent in the POST body in to PHP variables.
<?php// The raw post body is available in the variable $HTTP_RAW_POST_DATA$employee = json_decode($HTTP_RAW_POST_DATA); $sql = "SELECT * FROM employees WHERE employeeid = ".$employee['id']; ?>
The same using PHP streams.
<?php
$input = fopen("php://input", 'r);
$employee = json_decode(fread($input, 999));
$sql = "SELECT * FROM employees WHERE employeeid = ".$employee['id'];
?>
The example code below
<?php $indexedArray = array( "a", "b", "c" ); $result = json_encode($indexedArray); echo "Indexed array = " . $result . "<br/>"; $keyedArray = array( 1 => "a", 2 => "b", 3 => "c" ); $result = json_encode($keyedArray); echo "Keyed array = " . $result . "<br/>"; ?>
will render the following output:
Indexed array = [ "a", "b", "c" ]
Keyed array = { "1": "a", "2": "b", "3": "c" }
Circular references
Avoid serializing Java objects that contain circular references: JSON is used to represent data, not objects.
However, if circular references are detected, then the JSON API replaces the references with JavaScript-like pointers with a $jref prefix. For example:
public class A {
public B b;
}
public class B {
public Foo[] foos;
}
public class Foo {
public Bar bar;
}
public class Bar {
public Foo y;
}
...
A a = new A();
a.b = new B();
a.b.foos = new Foo[1];
Foo foo = new Foo();
Bar x = new Bar();
foo.bar = x;
x.y = foo;
a.b.foos[0] = foo;
String result = Json.encode(a);
// result = {"b" : {"foos" : [{"bar" : {"y" : "$jref:this.b.foos[0]"}}]}
The process of converting JSON to Java restores circular references using the same convention. Thus, round-trip conversion is loss-less.
JSON Renderer
WebSphere sMash provides a library of "renderers" for common output patterns. The common case of serializing a Java object to the response as a JSON representation is codified as a renderer.
Java example:
zput("/request/view", "JSON");
zput("/request/json/output", obj);
zero.core.views.ViewEngine.render();
Groovy example:
request.view="JSON" request.json.output=obj render()
PHP example:
<?php
$customer = array('name' => 'John Smith');
zput('/request/view', 'JSON');
zput('/request/json/output', $customer);
render_view();
?>
Output from example:
{"name":"John Smith"}
Pretty print
By default, WebSphere sMash serializes JSON in a compact format. This is efficient for transmission, but not tailored for human consumption. You can force a "pretty print" serialization by adding the following to your application's zero.config file:
/config/json/prettyPrint=true
{"name":"Bob"}
with pretty print:
{
"name": "Bob"
}
JSON Parser Callbacks
To create a custom Java object from a JSON encoded string one could use the decode method to pass in a zero.json.JsonParserCallback callback object. The boolean requireNamedObjects() method can be used to specify that the JSON parser should provide the name of the objects and/or arrays that are being created.
The callback is invoked with JsonObjectCallback createJsonObject(String name) when the parser encounters a JSON object. The Parser provides the name of the object if requireNamedObjects() returns true. The callback is invoked with JsonArrayCallback createJsonArray(String name) when the JSON parser encounters a JSON array in the
encoded stream.
The zero.json.JsonObjectCallback is invoked by the JSON parser in the course of processing an JSON object from
a stream. The put(String key, Object value) allows the implementer of this interface to update its internal representation
of the JSON object as the JSON object is parsed. The getObject() also provides the JSON parser with the representation of the JSON object to return.
The zero.json.JsonArrayCallback is invoked by the JSON parser in the course of processing an JSON object from
a stream. The add(Object value) allows the implementer of this interface to update its internal representation
of the JSON array as the JSON array is parsed. The getObject() also provides the JSON parser with the representation of the JSON array to return.
Custom converters
Custom converters are Java implementations of zero.json.converters.Converter:
public interface Converter extends Encoder {
/**
* Convert JSON representation to a Java object.
*/
public Object toObject(Object json);
}
public interface Encoder {
/**
* The implementer of this interface serializes a custom Java object as JSON using the serializer provided.
* The custom encoder is declared using the global context. This interface provides a object reference map
* as a parameter which can be used find and add references to Java objects that might already be serialized.
* It is a good practice to add a reference to the object being serialized prior to serializing it so that any circular
* references to the object can be resolved. Any complex (array or object) attribute added to the serialized stream
* should be updated in reference map with the object reference as a key and its JSON reference as its value.
*
* In general the JSON reference to complex attributes are computed as follows. Any array reference to a complex
* attribute concatenates the index of the attribute enclosed by "[" and "]" to the JSON reference of the containing
* object. For instance jref + "[" + index + "]". Any object reference to a complex attribute appends
* the name of the attribute separated by a period to the containing jref. For instance
* jref + "." + attributeName .
*
* @param object The object that needs to be serialized.
* @param refMap a reference Map of objects that have been serialized to their JSON references.
* This map should updated with the reference of the object being serialized and any of its elements.
* @param jref the JSON reference for the containing object.
* @param serializer the serializer object that needs to be used to create the custom json encoding for the object.
*/
public void write(Object obj, Map<Object, String> refMap, String jref, Serializer serializer) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException ;
}
Custom converters are registered via the GlobalContext as class name/converter pairs, such as:
/config/json/converters += {
"java.sql.Time" : "zero.json.converters.java.sql.TimeConverter",
"java.sql.Timestamp" : "zero.json.converters.java.sql.TimestampConverter"
"java.sql.Date" : "zero.json.converters.java.sql.DateConverter"
"java.util.Date" : "zero.json.converters.java.util.DateConverter"
}
/config/json/derivedConverters += {
"groovy.lang.Writable" : "zero.json.converters.groovy.lang.WritableConverter"
}
converters and derivedConverters differ in how they are matched. Converters registered under /config/json/converters are invoked when serializing from/parsing to an instance of the specified class; those under /config/json/derivedConverters are checked on superclasses and interfaces. converters are checked first.
JSONResultHandler for data access
The zero.data.php dependency provides a sample ResultHandler that is written in PHP. It is implemented in the
JSONResultHandler.php file and automatically included in the PHP include path whenever you have
zero.data.php as a dependency. This handler takes the result of a query operation, which is a JDBC ResultSet, and
renders it as JSON. Specifically, this is an array of maps with column names as keys.
See the Extending Data Access section for details.