|
|
|
JSON support
The zero.core package includes support for JavaScript Object Notation (JSON) data structures. The following sections of this article provide information about JSON support:
JSON APIs
zero.json.Json is the primary Zero 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, InputStream or Reader into an appropriate 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 decodeFromUrl(String urlString) throws IOException { ... }
/** Variation of the decode functions useful when decoding JSON objects into Maps and 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 decodeFromUrl(String urlString, boolean ordered) throws IOException { ... }
/** Serialize or encode a Java object as JSON data */
public static String encode(Object obj) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException{ ... }
public static void encode(Object obj, OutputStream os) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException{ ... }
public static void encode(Object obj, Writer writer) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException{ ... }
/** Encode a Java object with formatting */
public static String encode(Object obj, boolean formatted) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException{ ... }
public static void encode(Object obj, OutputStream os, boolean formatted) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException { ... }
public static void encode(Object obj, Writer writer, boolean formatted) throws IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException, IOException { ... }
/** 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
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.
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 JsonTypes 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
Zero 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:
zput("/request/view", "JSON");
zput("/request/json/output", $var);
render_view();
Pretty print
By default, Zero 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
For example, without pretty print:
{"name":"Bob"}
with pretty print:
{
"name": "Bob"
}
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.
|