About

Serializing/Deserializing Json in GWT

JSON & GWT

There was recently an interesting discussion in GWT Users group about best practices for serializing/deserializing JSON in the client side. This post aims to highlight its important points. There is so far three different ways of converting an object to JSON and back from the client side in GWT:


gwt-jackson framework:

gwt-jackson wraps some of the functionalities of the famous Jackson Library. It allows converting an object using an interface called ObjectMapper. The advantage of using gwt-jackson is that it takes care of serializing complex Objects such as Collections and Maps. It also allows fancy conversion using annotations such as @JsonProperty, and @JsonCreator. The only bummer of gwt-jackson is that it uses Generators which will be deprecated in the version 3.0 of GWT. It’s also worth noting that gwt-jackson is used by RestyGWT which is one of alternatives for making HTTP Requests from the client side.

Examples can be found in the Github Page: https://github.com/nmorel/gwt-jackson/tree/master/examples


using JavaScriptObject:

This is one of the traditional methods. JavaScriptObject makes use of JSNI to create a plain JS object. It can be extended and has a cast() method that allows “safe” casting the object to its sub-types. A JavaScriptObject can be converted to a JSON String using JsonUtils.stringify() method, and can be converted back using JsonUtils.safeEval(). JavaScriptObject works in conjunction with JsArray which represents a collection of JavaScriptObject, and extends JavaScriptObject. The only disadvantage of this method is the boilerplate associated with creating objects. For example:


public class Person extends JavaScriptObject {
	 //Constructor needs to be protected and zero-arguments
	  protected Person() { }

          // JSNI is used for setting/getting properties 
	  public final native String getFirstName() /*-{ return this.FirstName; }-*/;
	  public final native String getLastName()  /*-{ return this.LastName;  }-*/;
	  
	  public final native void setFirstName(String firstName) /*-{ this.FirstName = firstName; }-*/;
	  public final native void setLastName(String lastName)  /*-{ this.LastName = lastName; }-*/;
} 

Then:

 Person person = JavaScriptObject.createObject().cast();
				        JsArray array = JavaScriptObject.createArray().cast();
				        
				        person.setFirstName("first Name");
				        person.setLastName("last Name");
				        
				        array.push(person);
				        
				       
				        
				        GWT.log(JsonUtils.stringify(person));
				        GWT.log(JsonUtils.stringify(array));
				        

Result:

{"FirstName":"first Name","LastName":"last Name"}
[{"FirstName":"first Name","LastName":"last Name"}]


using JsInterop annotations:


JsInterop annotations allow treating a java type/class as a Javascript object, and exporting or importing functionalities to/from the application Js environment. Using JsInterop is the recommended method by some of the GWT project members (Thomas Broyer, Jens Nehlmeier), because JsInterop is an important part of the future of GWT, and it will be the main way of handling Javascript Objects from GWT. The only shortcoming for using JsInterop is that Elemental 2 is still in experimental phase, so until it gets stable. Developers are better off usng their own snippets for native Javascript utilities such as the Json class, for example:

@JsType(isNative=true, namespace=GLOBAL)
public class JSON {
	
	public native static String stringify(Object obj);
	
	public native static Object parse(String obj);

}

if our object looks like:

@JsType(isNative=true, namespace=GLOBAL, name="Object")
public class Record {
	
	String id;
	String date;
	String data;
	
	public Record() {
		
	}
	
}

Then :

 Record record = new Record();
  record.id = "1";
  record.date = "20";
  record.data = "30";

String json = JSON.stringify(recod);

 GWT.log(json);
// Result: {"id":"1","date":"20","data":"30"}			        			      

JsInterop is used by autorest-gwt, which is also one of the options for making HTTP calls, to serialize/serialize objects prior to making HTTP requests.

It is important to note that Maps are not handled by JSON.stringify() method, an attempt to stringify a Map throws the following error:

 Map mapTest = new HashMap();
				        mapTest.put("v1", "v2");
				        mapTest.put("v3", "v4");
 GWT.log(JSON.stringify(mapTest));
Uncaught TypeError: Converting circular structure to JSON

Converting a Collection such as an ArrayList does not throw any error, but creates additional JSON fields that the developper would want to get rid of:

  List test2 = new ArrayList();
				       
				       test2.add(record2);
				       test2.add(record);
 GWT.log(JSON.stringify(test2));

Result:

{"array_3_g$":[{"id":"1","date":"50","data":"90"},{"id":"1","date":"20","data":"30"}]}

The “array_3_g$” is added by GWT compiler for some reason, so the user needs to find a way to remove it for a clean conversion like:

{[{"id":"1","date":"50","data":"90"},{"id":"1","date":"20","data":"30"}]}

Plain Arrays is the only structure that is converted properly, so far.


Take Away:

Out of the three methods, JsInterop seems the most viable method for handling JSON. The main concern for now is handling objects such as Collections and Maps which require some further manual processing by the developer. Plain Arrays is the only structure that is converted properly for now, so developers can try to simplify or convert to Plain Arrays for a clean conversion.

While JavaScriptObject offers its own way of dealing with “lists” using JsArray. gwt-jackson remains the only option now that offers the conversion of Collections and Maps out of the box.

  • Amir

    @JsOverlay allows you to convert from JS array to java list. It also allows for smooth handling of enums.
    Alternatively, you can use a library such as https://github.com/GWTReact/gwt-interop-utils and use its Array class vs java.util.list. We have taken a hybrid approach and implemented both methods in our “POJOs”.

    • blogger

      I had gwt-interop-utils in mind, it is just that I have not tried yet. I will give it a try in some future post.

  • Thanks for sharing.

    To solve we changed
    jsArray from:
    public class JsArray extends JavaScriptObject {
    }

    to
    public class JsArray extends JavaScriptObject {
    }
    this allows us to deseralize from a JsInterop interface.

    so you can do this:
    JsArray records = JsonUtils.safeEval(response.getText()).cast();

    • Sorry my last post I had some generics in there that got deleted when I posted. Hope that makes sense.

    • blogger

      Sounds like a great solution to the collections problem. The comment system is not that elaborated, you need to use html :) . I think you meant:

      public class JsArray<T>> extends JavaScriptObject {
      }

      • Yes, that’s what I meant. Would be even better for jsArray to be defined like that. I’m not sure why it needs to be T extends JavaScriptObject.

For enquiries contact Francesca Tosi

Archive

Subscribe

Atom

Blog Partners