Sunday 20 November 2011

JSON Serialization

There are two JSON serialization engines in Liferay portal: the ‘manual’ serialization and the ‘loose’ serialization. Please note that these names are not something strict and widely recognized, they are here to distinguish these two engines.

Manual serialization #

Manual serialization begins with creation of JSONObject or JSONArray, using JSONFactoryUtil. These two are wrappers for JSON maps and arrays. After creation, it is all about putting map or array elements into created instances. At the end, invoking toString() method performs the serialization and JSON string is returned. Example:
JSONObject jsonObj = JSONFactoryUtil.createJSONObject();
jsonObj.put("additionalInfo", _additionalInfo);
jsonObj.put("companyId, _companyId);
...
return jsonObj.toString();
The main advantage of ‘manual’ engine is the complete control over what resulting JSON string will contain. However, this is also the major drawback when serializing java beans, since each property has to be added manually, both property name and value (as in above example). This is error prone and produces a lot of boiler plate code.
There is another potential flaw using JSONObjects: developer can start using them as a generic data container and return them as methods result. This is not such a good practice (at least in authors humble opinion:) - methods should return concrete types when possible. Moreover, by returning JSONObjects, the code becomes tightly-coupled with non-common type. Instead, methods should return either 1) a concrete type where it make sense, 2) json serialized string or 3) Map/List if the returned value has to be modified more. Again, JSONObjects are just util classes for JSON serialization and not data containers; their usage scope should be inside a method.
Manual serialization tool is built on top of json.org library and, therefore, it shares the same features. There are some sharp corners user should be aware of. One is that inner Map is not serialized as JSON map, but instead as a 'double' map: the first level contains information about map implementation type and then the second level contains actual map values. Another thing to be aware of is that class property (from the getClass() getter) is serialized as 'javaClass'.

Loose serialization #

Loose serialization uses a different approach – object is examined (using reflection) and automatically serialized. So, the basic usage is quite simple:
JSONFactoryUtil.looseSerialize(object);
And that is all! But that is just a beginning of loose serialization features.
One thing to remember is that, by default, all collections properties are NOT(loosely) serialized. To include collections in the serialization, we need to tell serializer which collections properties have to be included. For example:
JSONSerializer jsonSerializer = JSONFactoryUtil.createSerializer();
jsonSerilizer.include(“organizations”);
jsonSerilizer.seriliaze(user);
or, the same but using shortcut method:
JSONFactoryUtil.looseSerialize(user, “organizations”);
Similarly , some properties can be excluded using ‘exclude()’ method (there is not shortcut for this method). Including and excluding supports wildcards; a common example is: exclude(“.class”) to exclude class information in the resulting JSON string.
As seen above, loose serialization uses hints (what to include and exclude) to control the process of serialization. These hints are specified at the point of serialization.

Annotations #

The other way of specifying what to include and exclude is by using @JSON annotation. Simply annotate some getter method with @JSON or @JSON(include = false) to include/exclude that property from JSON serialization. In practice, user usually put this annotation on collection properties that has to be included (as collection properties are by default excluded).

Strict mode #

Sometimes, a significant set of properties has to be excluded from the serialization. For example, only top level properties of some model object have to be included, all others should be excluded. In such cases, it is more convenient to use something called the ‘strict’ mode. In strict JSON serialization mode we must annotate all properties that has to be serialized. Any property without an annotation is ignored. Example:
@JSON(strict = true)
public class UserModelImpl extends BaseModelImpl<User> implements UserModel {
....
@JSON
public String getUuid() {
...
@JSON
public long getUserId() {
...
All portals model object uses the strict mode, since they are generated by service builder. Service builder has support for JSON loose serialization,too. The json-enabled attribute of entitytag specifies whether or not the entity should be annotated for JSONserialization. By default, if the remote-service value is true, then thejson-enabled value is true. Column tag has the same attribute, json-enabled. It specifies whether ornot the column should be annotated for JSON serialization. By default, if thejson-enabled value in the entity element is true, then the json-enabled valuein the column element is true.

JSONSerializable interface #

Alternatively, objects can implement JSONSerilizable interface that contains single method: toJsonString(). Developer can put in there any code that generates custom JSON string from current object state. 

Flexjson #

Loose serialization is based on flexjson library and shares all its goods and bads. In general, the concept is fine, but it will not hurt to see some little imrovements (again, in authors humble opinion:).

No comments:

Post a Comment