Best Practice for JSON object recreation on client

1,644 views
Skip to first unread message

Max Fromberger

unread,
Aug 18, 2016, 3:33:37 AM8/18/16
to GWT Users
Hello everybody,

First of all a big THANKS to everyone involved in developing and supporting GWT.

please correct me wherever I am wrong:

In the past you used JsonUtils.safeEval() to create a JavaScriptObject from a JSON String. As i understand it, jsinterop annotations on classes not extending JavaScriptObject are now preferred over JavaScriptObjects.

What is the recommended, future-proof way of having a server-client common class (i.e. package "shared") that can be:

- created and accessed in a JVM servlet
- created and accessed from the client end
- serialized to JSON / deserialized from JSON to the specific class on the server (e.g. using GSON)
- serialized to JSON / deserialized from JSON to the specific class on the client

Background: Using WebSocket Servers to bidirectionally transfer JSONified objects. No GWT-RPC involved.
Trying to avoid duplicate environment specific classes, boilerplate code etc.

Thanks for every clue and please overlook me not having a native English interface. ;)



kind regards,
max


Vassilis Virvilis

unread,
Aug 18, 2016, 3:45:08 AM8/18/16
to google-we...@googlegroups.com
Hi,

I am using resty-gwt. Resty-gwt had his own (forked from jackson I believe) json serialization classes. But now Rest-gwt is switching to gwt-jackson. I haven't managed to get gwt-jackson to work with my setup (yet) but looks like a valid path forward.

In the server I am using CXF and the normal jackson. I am not saying everything is perfect but most of my objects are transferred without a fuss. In some cases where the object is complicated (e.g. Collection<Map<T, Collection<V>> members or inheritance) I have to provide a custom provider that knows how to deal. However this was a one time pain for me. Now I have forgotten all the terrifying details and it just works.

I am sure there are other solutions like GSON which I don't have any experience with.

Furthermore with the upcoming GWT3 which will deprecate generators all these projects have a scheduled rewrite pending... So who knows?

    Vassilis



--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

Max Fromberger

unread,
Aug 18, 2016, 3:50:42 AM8/18/16
to GWT Users
Hello Vassilis,

thanks for this contribution.
This raises another question: is there a way to solve the above described tasks that is GWT 3 proof and already feasible in GWT 2.8?

kind regards,
max
To post to this group, send email to google-we...@googlegroups.com.



--
Vassilis Virvilis

Thomas Broyer

unread,
Aug 18, 2016, 4:41:28 AM8/18/16
to GWT Users
Have you tried something like:

@JsType(isNative=true,namespace=GLOBAL,name="Object")
class MyObj {
String str;
int num;
}

Max Fromberger

unread,
Aug 18, 2016, 4:54:50 AM8/18/16
to GWT Users
Hi Thomas,

wouldn't that still require MyObj to extend JavaScriptObject in order to be deserialized on the client?
Thanks for your participation.

Kind regards,
max

Thomas Broyer

unread,
Aug 18, 2016, 4:58:48 AM8/18/16
to GWT Users
Why would it? You may want to use JsInterop to call JSON.parse() instead of using JsonUtils and casts from JavaScriptObject, but I believe it'd just work otherwise (can't try it ATM though)

Vassilis Virvilis

unread,
Aug 18, 2016, 5:08:52 AM8/18/16
to google-we...@googlegroups.com
Generally the GWT guys are careful and I am sure they will provide a way to migrate. If I understand correctly the mechanism that will replace generators (GWT.create()) is called APT and is based on annotations. In my codebase I don't have the need and so I don't really know but I am using libraries that require some way to create code (self-inspection) such are resty-gwt, event-binder and gwt-jackson.

I don't know if APT is currently available in GWT but I don't think so. I believe that APT and GWT generators should co-exist for at least one GWT release for migration to happen.

For CSS vs GSS I know this is already happening. GSS is available in GWT 2.8 and CSS will be deprecated in the next version,

    Vassilis

Jens

unread,
Aug 18, 2016, 5:35:34 AM8/18/16
to GWT Users

Why would it? You may want to use JsInterop to call JSON.parse() instead of using JsonUtils and casts from JavaScriptObject, but I believe it'd just work otherwise (can't try it ATM though)

You can not use a Java Collection as field so you would need some conversion to JsArray that JsInterop / JSON.parse() can handle but otherwise it should work well.

-- J.

Paul Stockley

unread,
Aug 18, 2016, 9:10:59 AM8/18/16
to GWT Users
I wrote a set of utilities using the new JsInterop capability in 2.8 that provides one way of handling JSON. Take a look at the documentation here https://github.com/GWTReact/gwt-interop-utils/blob/master/DOCUMENTATION.md

We use Jackson on the server for handling the translation from JSON to Java objects

Luke Last

unread,
Aug 19, 2016, 1:56:51 AM8/19/16
to GWT Users
I use jackson/gson on the server to serialize and then gwt-jackson on the client to deserialize a shared pojo. I'm interested in finding out if JsInterop gives better performance though.

Thomas Broyer

unread,
Aug 19, 2016, 2:58:11 AM8/19/16
to GWT Users
Or you could use an array instead of a collection.

zakaria amine

unread,
Aug 19, 2016, 5:43:12 AM8/19/16
to GWT Users
I have tried something like: 

@JsType(namespace=GLOBAL)
public class Record {
String id;
String date;
String data;
public Record() {
}
}

As mentioned above, I created a JsInterop wrapper for the JSON class in javascript :

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

}

After calling : 

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

GWT.log(JSON.stringify(record));

I got : 

{"id_1_g$":"1","date_1_g$":"20","data_1_g$":"30"}

I am not sure why does GWT adds _1_g$ to all properties. the solution is to annotate properties with @JsProperty(name="property name") 

Jens

unread,
Aug 19, 2016, 5:51:35 AM8/19/16
to GWT Users


Am Freitag, 19. August 2016 11:43:12 UTC+2 schrieb zakaria amine:
I have tried something like: 

@JsType(namespace=GLOBAL)
public class Record {
String id;
String date;
String data;
public Record() {
}
}

By default @JsType property "isNative" is false, so your Record class is a non-native class that might get exported to JS if you use -generateJsInteropExports during compilation. If you don't use that flag the @JsType is treated as a normal class I guess.

You should use @JsType(isNative=true, namespace=GLOBAL, name="Object") so that your Record class becomes a plain JavaScript object

-- J.

zakaria amine

unread,
Aug 19, 2016, 6:05:32 AM8/19/16
to GWT Users
It works. I prefer your solution.

zakaria amine

unread,
Aug 19, 2016, 9:30:19 AM8/19/16
to GWT Users
I also tried to convert back to the original object: 

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

}

and then: 
//....

  Record converted = (Record) JSON.parse(json);

and it works just fine. why would we need something like gwt-jackson anymore? 

Vassilis Virvilis

unread,
Aug 19, 2016, 9:38:28 AM8/19/16
to google-we...@googlegroups.com
How about transmitting nested Collections, Map, complex inheritance and generics?

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.



--
Vassilis Virvilis

Ignacio Baca Moreno-Torres

unread,
Aug 19, 2016, 10:02:33 AM8/19/16
to GWT Users
IMHO supporting the whole collection frameworks is just an unnecessary complication. Just use plain array, not generics need, and now that stream are supported in GWT you has no excuse to use arrays. The inheritance is not solved in JsInterop for now, just try to avoid.
To post to this group, send email to google-we...@googlegroups.com.



--
Vassilis Virvilis

Vassilis Virvilis

unread,
Aug 19, 2016, 10:10:30 AM8/19/16
to google-we...@googlegroups.com
This makes sense for newer projects maybe.

I already have a codebase and making trampolines to convert collections and maps to arrays and don't know what is a terrifying option.

I prefer to depend on gwt-jackson (which doesn't work for me - resty-gwt works - resty-gwt is switching to gwt-jackson - eventually gwt-jackson will work for me) especially if I am using jackson already in the server,


To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.



--
Vassilis Virvilis

Ignacio Baca Moreno-Torres

unread,
Aug 19, 2016, 1:59:49 PM8/19/16
to GWT Users
Migrating everything is not a good idea, but you should give a try to arrays in new models or some parts of your application. We found that we frequently end up using Immutable collections and FluentIterable, and this is almost the same that using arrays and streams. As you said, migrating a whole project is too complicated, but you can just start using and progressively applying to the whole API if this actually works for you. In 3 years we past from using RequestFactory, to RestyGWT to almost plain request + jsinterop (actually autorest-gwt). Maybe using plain objects and arrays doesn't fit in your project, but you really should try.

Emm... and a bit of context; I'm try to justify that the complexity added during parsing/encoding to support collections and generics really do not worth (depends on projects) if your API use DTOs and you have streams available. In our project the DTOs classes end up as a scheme definition, defining the name and type of each property and with a minimal overhead during parsing/encoding in JRE, GWT and even Android (https://github.com/ibaca/autorest-nominatim-example/).
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

Vassilis Virvilis

unread,
Aug 19, 2016, 2:04:37 PM8/19/16
to google-we...@googlegroups.com
Thanks for the write up. I would definitely have it in mind.

BTW can you expand a bit on the stream thingy? Is there a link somewhere to read about?

    Vassilis

To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.

To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.



--
Vassilis Virvilis

Ignacio Baca Moreno-Torres

unread,
Aug 19, 2016, 4:06:52 PM8/19/16
to GWT Users
I mean java8 Streams (https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html). And I said that is the same as Immutable+FluentIterable because you can use Stream.of(values[]). And IMO this makes more sense in a DTO than using the interfaces on the collection frameworks (list, set, map, and worst with specific types like ArrayList, EnumSet, etc), because whats means a set in a DTO (I mean in the JSON), or worst an EnumSet? this information is not related to the schema of the transferred data, and I think that this makes the encoding/decoding much more complex. And with Streams you can get back to the java collection whenever you really need it quite easy, for example if you have 'String[] types' an array of enum names of TypeEnum, you can get the Set using Stream.of(types).map(TypeEnum::valueOf).colllect(toSet());
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.



--
Vassilis Virvilis

Thomas Broyer

unread,
Aug 19, 2016, 6:54:24 PM8/19/16
to GWT Users
I think the crux is thinking in terms of messages (payloads) rather than "domain objects". This applies to DTOs vs domain objects too, with RPC or RequestFactory or whatever.

Ignacio Baca Moreno-Torres

unread,
Aug 22, 2016, 3:25:31 AM8/22/16
to GWT Users
"domain object" vs "message payload"; Yes, this is the point. We came from RequestFactory which works quite good with "domain object" models, we use one model for each type, this model is the same in the server and the client, and all views use the same model.

So, responding to the 'JSON object recreation in the client'; JsInterop makes trivial Object recreation, you map your messages in a very explicit and clean manner having almost zero overhead, but you should take care of this "domain object" vs "message payload" model strategy. JsInterop doesn't work well with "domain object" models (so questions like how can you return a Set or a Map are currently hard to answer, and makes no much sense in the "message payload" side), but it will work much better with "message payloads". We are getting really good result using plain classes without methods. But it's important to think on terms of "message payloads", you should try to avoid fancy things, using only primitives (plus Boolean, Double and String), arrays or other @JsType models. We end up with lot of services like this. This models frequently do not need any special annotation, but are so simple that most JSON encoder works correctly (Jackson, gson, moshi...) and obviously JsInterop.

@AutoRestGwt
@Path(RESOURCE_TOOLS)
@Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public interface ToolsRestService {

@GET @Path("build")
Single<BuildInfo> getBuildInfo();

@JsType(namespace = GLOBAL, name = "Object", isNative = true)
class BuildInfo {
public int buildDate;
public String version;
public String commitId;
public String javaVersion;
}

@GET @Path("system")
Single<SystemInfo> getSystemInfo();

@JsType(namespace = GLOBAL, name = "Object", isNative = true)
class SystemInfo {
public double startupTime;
public double totalMemory;
public double freeMemory;
public double usedMemory;
}
}

I like to think of this classes as something like JSON (JavaScript Object Notation) but in Java and to define the scheme instead of the object itself so JOSN (Java Object Scheme Notation), i.e. a subset of Java just to define objects schemes. 
Reply all
Reply to author
Forward
0 new messages