I'm trying to manually serialize an object to a string on the server
and deserialize it back again on the client (for reasons I won't go
into unless someone's interested). I figured it would be sensible to
use GWT's serialization for this, but I'm a bit stumped.
On the server side I'm calling RPC.encodeResponseForSuccess() to get
the string. All good, I think.
I thought that on the client side I should call JSONParser.parse(),
but that leaves me with a JSONObject. How do I do the last part -
creating my object structure from the JSONObject? I don't want to
transfer the values for each field in the object tree manually!
Obviously GWT's doing this in normal RPC so the code must be there
somewhere.
Or am I barking up the wrong tree altogether?
Cheers,
Adrian
if you passing back JSON object as string in your RPC calls you can
bind it on client side to your classes using JSIO (javascript interop)
GWT lib:
it works just fine for JSON bojects bindings (that is not
serialization):
http://code.google.com/p/gwt-api-interop/
http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/9f209b3a053ee836/72f5272f2f19b539?lnk=gst&q=JSIO#72f5272f2f19b539
you can also use more traditional binding (something you are stuck
currently with JSONObject at hand):
EXAMPLE CODE (test code):
package code.google.com.gwt.flickr.api.impl;
import com.google.gwt.json.client.JSONObject;
public abstract class ResultsWrapper {
protected JSONObject json;
public ResultsWrapper(JSONObject json) {
this.json = json;
}
public final String getJSONString() {
return json.toString();
}
}
package code.google.com.gwt.flickr.api.impl;
import com.google.gwt.json.client.JSONObject;
public class GroupWrapper extends ResultsWrapper {
private static final String NSID_KEY = "nsid";
private static final String URL_KEY = "url";
private static final String GROUP_KEY = "group";
public GroupWrapper(JSONObject json) {
super(json.get(GROUP_KEY).isObject());
}
public String getNSID() {
return json.get(NSID_KEY).isString().stringValue();
}
public String getURL() {
return json.get(URL_KEY).isString().stringValue();
}
}
regards,
Peter
Thanks for that. I checked out JSIO but the idea of those extra
interfaces and annotations still bugged me, although granted it's
easier than doing it manually.
I've ended up hooking into the code GWT's already generated by working
within the existing RPC mechanism. Essentially I defined a separate
LocalService and set its setServiceEntryPoint URL to something like
"local:...". I then extended HttpRequest to handle these local URLs,
returning the encoded JSON string (which I already have) almost
immediately. Works well, although I won't tell you the time I spent
exploring other avenues first (grimace).
This is all in the name of saving an extra request at startup to fetch
my setup data. (I'm doing iphone development - every request counts!)
Instead of fetching it by RPC once the module's loaded, the server
injects the JSON string into the initial HTML page.
Gratifyingly, I have my webapp's startup down to two requests, images
and CSS included :-)
I am indeed using the ImmutableResourceBundle and StylesheetInjector
from the incubator for static resources like images and CSS. Great.
The setup data I refer to above is dynamic though, hence my need for
another solution. Some code snippets follow - they could do with some
refinement but you'll get the idea:
public class HttpRequestWithLocal extends HTTPRequestImpl {
// TODO Allow for the fact there's an IE-specific subclass of
HTTPRequestImpl
public boolean asyncPost(String user, String pwd, String url,
String postData, final ResponseTextHandler handler) {
if (url.startsWith("local:")) {
String name = url.substring(6);
final String data = getData(name);
DeferredCommand.addCommand(new Command() {
public void execute() {
handler.onCompletion(data);
}
});
return true;
}
else {
return super.asyncPost(user, pwd, url, postData, handler);
}
}
private static native String getData(String name) /*-{
return top[name];
}-*/ ;
}
--------
public class LocalServiceFactory {
private static LocalServiceAsync ourInstance = null;
public static synchronized LocalServiceAsync getInstance() {
if (ourInstance == null) {
ourInstance = (LocalServiceAsync)
GWT.create(LocalService.class);
((ServiceDefTarget)
ourInstance).setServiceEntryPoint("local:___initialData___");
}
return ourInstance;
}
}
--------
And in my servlet:
private String insertDataIntoHtml(String html) throws
SerializationException, NoSuchMethodException {
int i = html.indexOf("<head>") + 6;
InitialData data = myService.getInitialData();
String encoded =
RPC.encodeResponseForSuccess(YojimboService.class.getMethod("getInitialData"),
data);
// change \ into \\ to allow for the fact that it's going
through Javascript
encoded = encoded.replaceAll("\\\\", "\\\\\\\\");
String script = "<script language='javascript'>
\ntop.___initialData___ = '" + encoded + "';\n</script>";
html = html.substring(0, i) + script + html.substring(i);
return html;
}
I see you're binding your startup data to source injected on client
request (first hit request), is that correct?
I've been testing and I think will be used similiar solution though I
think reather simpler:
#1
server part is simple, just write "script" tag into your web page
content
#2
assuming that for example config data is like:
<script type="javascript">
var com = {};
com.mycompany = {};
com.mycompany.project = {};
com.mycompany.project.config = {
"firstName":"Peter",
"lastName":"Blazejewicz",
"isAdmin":true
};
</script>
#3
i can either access it using gwt Dictionary object:
http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/gwt/i18n/client/Dictionary.html
(look, no binding at all, just accessing hashed object)
#4
or use GWT JSIO bindings that way (fully binding to whatever class is
required),
here example config class:
package com.mycompany.project.client;
import com.google.gwt.jsio.client.JSWrapper;
/**
* @gwt.beanProperties
* @gwt.global $wnd.com.mycompany.project.config
*/
public abstract class Config implements JSWrapper {
public abstract String getFirstName();
public abstract String getLastName();
/**
* @gwt.fieldName isAdmin
*/
public abstract boolean isAdmin();
/*@Override*/
public String toString() {
return getFirstName()+" "+getLastName()+" is admin: "+isAdmin();
}
}
(note: javabean, automatically binded to existing javascript object
via gwt.global)
#5
and use it for example that way:
package com.mycompany.project.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
public class JSONConfig implements EntryPoint {
public void onModuleLoad() {
RootPanel rootPanel = RootPanel.get();
// VIEW
final Label firstNameLbl = new Label("First name: ");
rootPanel.add(firstNameLbl);
final Label lastNameLbl = new Label("Last name: ");
rootPanel.add(lastNameLbl);
final CheckBox isAdminCbx = new CheckBox();
rootPanel.add(isAdminCbx);
isAdminCbx.setText("Is in admin role:");
// BINDINGS
Config config = (Config) GWT.create(Config.class);
firstNameLbl.setText(firstNameLbl.getText() +
config.getFirstName());
lastNameLbl.setText(lastNameLbl.getText() + config.getLastName());
isAdminCbx.setChecked(config.isAdmin());
//
Window.alert(config.toString());
}
}
regards,
Peter
Yes, that's right.
> #2
> assuming that for example config data is like:
> <script type="javascript">
> var com = {};
> com.mycompany = {};
> com.mycompany.project = {};
> com.mycompany.project.config = {
> "firstName":"Peter",
> "lastName":"Blazejewicz",
> "isAdmin":true};
>
> </script>
This looks good in the simple case, but the structure of my initial
data is more complicated - nested objects, lists etc - and I'd rather
not have to convert it to Javascript. Or perhaps there's an automated
way of doing this?
> #3
> i can either access it using gwt Dictionary object:http://google-web-toolkit.googlecode.com/svn/javadoc/1.4/com/google/g...
> (look, no binding at all, just accessing hashed object)
Again, doesn't suit my case because the complexity of my data. Nice to
know about this though.
>
> #4
> or use GWT JSIO bindings that way (fully binding to whatever class is
> required),
> here example config class:
>
> package com.mycompany.project.client;
>
> import com.google.gwt.jsio.client.JSWrapper;
> /**
> * @gwt.beanProperties
> * @gwt.global $wnd.com.mycompany.project.config
> */
> public abstract class Config implements JSWrapper {
> public abstract String getFirstName();
> public abstract String getLastName();
> /**
> * @gwt.fieldName isAdmin
> */
> public abstract boolean isAdmin();
> /*@Override*/
> public String toString() {
> return getFirstName()+" "+getLastName()+" is admin: "+isAdmin();
> }
>
> }
>
> (note: javabean, automatically binded to existing javascript object
> via gwt.global)
Hey I like the way you can bind from the global here.
My problem with the JSWrapper is that it interferes with my class
structure too much - I use concrete instances of these classes on the
server and I don't want them dependent on JSWrapper, and I do want to
be able to instantiate them with "new". A shame we can't use an
annotation instead of JSWrapper, but I guess GWT doesn't work like
that.
#2
you can use XStream:
http://xstream.codehaus.org/
to serialize your comples Beans into JSON directly,
that is used n Grails I'm using:
http://grails.org/Converters+Plugin
it can be either returned as results of RPC/Ajax call or simply
rendered (using so called "codecs") into page html content directly,
eg. within head tag or body tag,
I'm using JSIO for Flickr api, which is far more complex data
structure then usually used in java world pojo/rpcs i think,
The good point of using JSIO is that you can:
- handle exceptions
- handle generated nulls
if some data is not available within complex data (so you don't really
needs to use various beans)
WE DON"T create javascript, it is done by server by simply evaluating
"<script>" tag,
so my server simply wrote string, mark response type as "application/
json", or whatever required,
> My problem with the JSWrapper is that it interferes with my class
> structure too much - I use concrete instances of these classes on the
> server and I don't want them dependent on JSWrapper, and I do want to
> be able to instantiate them with "new". A shame we can't use an
> annotation instead of JSWrapper, but I guess GWT doesn't work like
> that.
Because your are not using GWT RPC mechanism your we don't pass any
"instance" in generated html cotnent, correct?
@gwt.global just binds wrapper constructor to existing javascript
object without using new instance operator
(it can be done also by setting javascript as target for wrapper) I
think,
regards,
Peter
On Nov 9, 12:26 pm, Peter Blazejewicz <peter.blazejew...@gmail.com>
I have the same need as you and looked at the gwt-rocket project just
this afternoon. I was able to get the client code working (although I
found a few bugs), but as far as I could tell the rocket library does
not contain any way to serialize the java object into JSON on the
server.
You serialize an object with:
SomeClass instance = ....
JsonSerializer serializer = (JsonSerializer)
GWT.create( SomeClass.class );
JSONValue json = serializer.writeJson( instance );
And the deferred binding only works on the client. If there is a way
to do this on the server I'd like to know.
Nathan
On Nov 9, 4:39 pm, Peter Blazejewicz <peter.blazejew...@gmail.com>
wrote:
On Nov 9, 11:51 pm, Nathan Egge <nathan.e...@gmail.com> wrote:
> [...] way to serialize the java object into JSON on the
> server.
see previous answer on serialization on server side,
we can use already existing libraries, for example XStream'
http://xstream.codehaus.org/
which outputs either xml or json,
regards,
Peter
Nope, not doing that.
> What people usually do in this scenario
> is to stash the string into the outer HTML somewhere and then on they use a
> ClientSerializationStreamReader on the client to deserialize the string.
> Note this is not technically supported and it might change.
Well, yes, but ClientSerializationStreamReader doesn't know about
objects, so now we're back to processing things field-by-field, object-
by-object. Bad for complex data. It seems preferable to discover an
elegant solution to the problem rather than just shutting up and doing
the grunt work. After all, that's what GWT's all about.
The problem: I want to turn my object structure into a block of data
on the server, pass it to the client and turn the data back into the
object structure.
Oh, whaddya know, GWT does exactly this already! Surely it makes sense
to tap in to this existing code then - no drudgery, no extra
libraries, no extra interfaces or annotations - and besides, the code
will be smaller.
So, on the server side we have access to RPC.encodeResponseForSuccess.
Easy. On the client, though, the code to re-create the object
structure is generated by GWT directly into the service proxies -
hence my jiggery-pokery above to tap into that code. What would be
really nice is if GWT gave us a way to call already-existing mechanism
ourselves in an ad-hoc way.
> What people usually do in this scenarioWell, yes, but ClientSerializationStreamReader doesn't know about
> is to stash the string into the outer HTML somewhere and then on they use a
> ClientSerializationStreamReader on the client to deserialize the string.
> Note this is not technically supported and it might change.
objects, so now we're back to processing things field-by-field, object-
by-object. Bad for complex data. It seems preferable to discover an
elegant solution to the problem rather than just shutting up and doing
the grunt work. After all, that's what GWT's all about.
The problem: I want to turn my object structure into a block of data
on the server, pass it to the client and turn the data back into the
object structure.
Oh, whaddya know, GWT does exactly this already! Surely it makes sense
to tap in to this existing code then - no drudgery, no extra
libraries, no extra interfaces or annotations - and besides, the code
will be smaller.
So, on the server side we have access to RPC.encodeResponseForSuccess .
Easy. On the client, though, the code to re-create the object
structure is generated by GWT directly into the service proxies -
hence my jiggery-pokery above to tap into that code. What would be
really nice is if GWT gave us a way to call already-existing mechanism
ourselves in an ad-hoc way.
I totally agree with this statement. There should be a clean way to
do this sort of thing that is natural to the programmer and doesn't
require a lot of extra work or additional libraries. This problem is
common enough that there should be an out of the box solution built
right into GWT.
> Oh, whaddya know, GWT does exactly this already! Surely it makes sense
> to tap in to this existing code then - no drudgery, no extra
> libraries, no extra interfaces or annotations - and besides, the code
> will be smaller.
Here is where I disagree slightly. JSON is enough of a standard that
allowing for serialization support directly within GWT would make
interoperability with other applications a lot easier. The existing
GWT serialization is smart enough to know how to take annotations on
your java.io.Serializable classes and generate both server and client
side code. Something like this seems ideal:
/**
* @gwt.typeArgs <model.Project>
* @gwt.encoding
* type="json"
* @gwt.json-encoding
* label="projects"
*/
protected List projects;
Then you could use the same smarts that handles the serialization with
plugable encoding engines. One for JSON, one for GWT's RPC bit, etc.
If you don't like what already exists, its possible to write your own
set of Serializers that tap into an existing mechanism.
Nathan
The above actually doesn't make any sense. If you implement
Serializable, it should be possible to serialize/deserialze the object
hierarchy as JSON without requiring any annotations. You might
introduce an annotation for renaming purposes. The above would be:
public class UserData implements Serializable {
/**
* @gwt.typeArgs <myApp.gwt.project.client.ProjectData>
* @gwt.json
* field="myProjects"
*/
protected List projects;
...
};
Then on your Service interface:
public interface UserService extends RemoteService {
/**
* @gwt.typeArgs <myApp.gwt.user.client.UserData>
* @gwt.serializer
* class="com.google.gwt.json.client.JSONSerializer"
*/
public List retrieveUsers();
...
};
Which would allow you to advise the compiler which pluggable
serialization mechanism to use.
For added fun, you could then reuse the same serialization mechanism
to do serialization with in the client:
JSONSerializer serializer=(JSONSerializer)GWT.create(UserData.class);
UserData user=(UserData)serializer.toObject(jsonString);
And once java5 support is added, it would be possible to decode a list
of Serialized objects without needing a wrapper object:
JSONSerializer
serializer=(JSONSerializer)GWT.create(List<UserData>.class);
List users=(List)serializer.toObject(jsonString);
I'm still learning how much of GWT works, but it seems like it would
be possible to do this. I'd appreciate any comments or suggestions,
I'd like to write a patch but still don't fully understand the
compiler magic.
Nathan
On Nov 10, 9:51 am, Nathan Egge <nathan.e...@gmail.com> wrote:
> Hi Peter,
>
> I have the same need as you and looked at the gwt-rocket project just
> this afternoon. I was able to get the client code working (although I
> found a few bugs), but as far as I could tell the rocket library does
> not contain any way to serialize the java object into JSON on the
> server.
Which bugs did you find ?
>
> You serialize an object with:
>
> SomeClass instance = ....
> JsonSerializer serializer = (JsonSerializer)
> GWT.create( SomeClass.class );
> JSONValue json = serializer.writeJson( instance );
>
> And the deferred binding only works on the client. If there is a way
> to do this on the server I'd like to know.
>
Take a look at the FlexJson project. Its got lots of nifty features
and does the job of serializing a java pojo to json.
The only reason client side stuff was created was because reflection
is not available on the client so this must be overcome invoking a
generator (the reason for the deferred binding step ).
> Which bugs did you find ?
I have an existing object hierarchy that extends
java.io.Serializable. This includes an abstract base class that
contains common data fields, like an id (long) and a label (String)
which I made implement rocket.json.client.JsonSerializable. Because
it is abstract it does not have a constructor, but when I go to
compile my application the JsonSerializer code thinks it needs to
instantiate it. I believe there was a related issue with objects that
don't contain an default/empty constructor, but I'll need to check.
Nathan
On the server:
public MyObjectController() {
xstream=new XStream(new JettisonMappedXmlDriver());
xstream.alias("",List.class);
xstream.alias("myObjects",MyObject.class);
}
public ModelAndView handleRequest(HttpServletRequest
_request,HttpServletResponse _response) throws Exception {
List myObjects=myObjectService.retrieveAll();
String json=xstream.toXML(myObjects);
// hack to exclude the first element
Map model=new HashMap();
model.put("myObjects",json.substring(4,json.length()-1));
return(new ModelAndView("myObjects.jsp",model));
}
In myObjects.jsp
<div id="myObjects" style="display: none;">${myObjects}</div>
Then on the client, I had to create a holder object for a List so I
had some place to put my annotations:
import java.util.List;
import rocket.json.client.JsonSerializable;
public class MyObjectList implements JsonSerializable {
/**
* @jsonSerialization-javascriptPropertyName myObjects
* @jsonSerialization-listElementType myApp.client.myObject
*/
public List myObjects;
public MyObjectList {}
};
And finally in onModuleLoad() ...
private static native String getById(String _id) /*-{
var element=$doc.getElementById(_id);
if (element==null)
return "";
return element.innerHTML;
}-*/;
private static native JavaScriptObject evaluate(String _jsonString) /*-
{
return eval('('+_jsonString+')');
}-*/;
public void onModuleLoad() {
JsonSerializer
serializer=(JsonSerializer)GWT.create(MyObjectList.class);
JSONObject myObjectsJSON=new
JSONObject(evaluate(getById("myObjects")));
List
myObjects=((MyObjectList)serializer.readObject(myObjectsJSON)).myObjects();
...
};
Hope this is useful to someone. With the exception of the trickery in
XStream and the fact that I had to bastardize my object tree for use
with gwt-rocket JsonSerializer, I am happy with this solution. And it
only took me a day to get everything up and running.
Nathan
On Nov 11, 6:17 pm, Nathan Egge <nathan.e...@gmail.com> wrote:
> <div id="myObjects" style="display: none;">${myObjects}</div>
wouldn't it better to render script tag within head or body?
<script language="javascript">
<!--
var myObjects = ${myObjects};
//--></script>
and use $wnd.myObjects ?
> // hack to exclude the first element
> Map model=new HashMap();
can you post example JSON structure? it could be probably client side
work to get correct element in quick dot like notation usage,
regards,
Peter
I didnt attempt to make mapping between json and java polymorphic. The
design assumes there is only ever a one to one mapping between json
objects within json and a java field. And because abstract types
cannot be instantiated it fails. I suppose the option is to complain
if the generator encounters an abstract field ( oops it should have
complained ) or add an annotation that says the actual type that
should be placed in the field with the abstract type.
Comments are welcome with regards to whether you think adding an
annotation for a field with an abstract type, solves your problem...
I actually am not talking about an object hierarchy where an object
field is an abstract class. I am talking about just Serializing a
concrete class that has fields that are contained in an abstract base
class. In this case you should be able to reconstruct the instance
based on what class is specified in the annotation (e.g. if you
annotate a List class to contain some concrete class that extends an
abstract base class with fields that have also been annotated).
I know that there are extensions to the JSON spec that allow you to
describe the class of a field, which would allow you to specify which
superclass of a particular abstract field you are talking about. It
would be interesting to write both sides of the serialization scheme
so that you could insert such markers to handle those cases. You
could also add markers for references to already described objects
(like YAML does).
Nathan
On Nov 13, 8:40 am, Nathan Egge <nathan.e...@gmail.com> wrote:
> Hi mP,
>
> I actually am not talking about an object hierarchy where an object
> field is an abstract class. I am talking about just Serializing a
> concrete class that has fields that are contained in an abstract base
> class. In this case you should be able to reconstruct the instance
> based on what class is specified in the annotation (e.g. if you
> annotate a List class to contain some concrete class that extends an
> abstract base class with fields that have also been annotated).
>
Can you give an exampl eof your comments ? Im bit unsure of why you
are saying abstract class a few times.