cross domain JSON retrieval, some notes

18 views
Skip to first unread message

kbs

unread,
Aug 16, 2006, 7:08:49 PM8/16/06
to Google Web Toolkit
I've been playing with some code to inject dynamic script tags and
retrieve JSON data from arbitrary servers. Here are some notes and
sample code, in the hopes that it will be useful to someone. Also
looking for better ways and code to solve this problem.

Some background:
The cross domain JSON object retrieval method presumes a JSON service
similar to the yahoo callback api. eg:

http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Madonna&results=2&output=json&callback=ws_results

where the service wraps the json object in a function provided by the
caller.

The method inserts a script element pointing to the URL, which causes
the function to be called with the JSON object when it is loaded. Of
course, the most interesting part of all this is that it can load data
from other hosts.

Notes:
I realized that the current JSON parser doesn't permit passing in a
native JavaScriptObject in the public method. Moreover, it doesn't seem
to do the right thing when the browser natively creates objects, as is
the case when the dynamic script trick is used. (The normal mechanism
uses eval() of course.) Specifically, JSONParser.isJSONObject() checks
for foo instanceof Object, which returns false on some browsers.

So in the sample code, I just return the native javascript object
directly to avoid complexifying the code with workarounds for the above
problem.

Sample code:
src/kbs/client/CXDomainRequest.java:
/**
*
* (c) 2006 KB Sriram. You may use or modify this code
* as you wish, as long as you leave this copyright
* notice in place.
*
* Simple utility to implement cross-domain json object
* retrieval via a dynamic script tag.
*
* Implementation notes:
*
* The code assumes the remote service is capable of taking
* a function name, and returns a script that calls the
* function with the json object. (c.f. yahoo api,
* http://developer.yahoo.com/common/json.html#callbackparam)
*
* A temporary function is created within the $wnd, and a temporary
* dynamic script tag is added to the <head> element of the $doc.
* The name of the temporary function is passed to the remote service.
*
* When called, the temporary function removes itself and the script
tag,
* then calls the handler.
*
* NB: the implementation returns a native javascript object,
* which you probably want to extract it into a JSONObject. Except
* that the GWT parser doesn't expose a way to pass a native object
* which is converted into java style JSON objects.
*
* Use at your own risk -- if the remote service doesn't return
* a proper json object, things don't get cleaned out. It
* Works For Me (TM) on the browsers I tested.
*/

import com.google.gwt.core.client.JavaScriptObject;

public class CXDomainRequest
{
/**
* @param urlpfx is the url to connect to,
* except for the name of the callback function.
* The code will append an appropriate name to the URL.
*
* @param handler will be called when the object
* is returned.
*/
public final static void asyncCall
(String urlpfx, IXDomainRequestHandler handler)
{ generateScript(urlpfx, handler, s_id++); }

////////////////// private helpers from here
private final static native void generateScript
(String url, IXDomainRequestHandler handler, int id)
/*-{
var head = $doc.getElementsByTagName("head").item(0);
var script = $doc.createElement("script");
// Create a little hash that contains pending callbacks,
// which we delete as and when replies show up.
if (!$wnd.__xdomcb) { $wnd.__xdomcb = new Object(); }
$wnd.__xdomcb[id] = function (json) {
// Clean up behind ourselves
var tmp = $wnd.__xdomcb;
delete (tmp[id]);
head.removeChild(script);

handler.@kbs.client.IXDomainRequestHandler::handle(Lcom/google/gwt/core/client/JavaScriptObject;)(json);
}
script.setAttribute("type", "text/javascript");
script.setAttribute("src", url+"__xdomcb["+id+"]");
head.appendChild(script);
}-*/;

// A unique counter.
private static int s_id = 0;
}

src/kbs/client/IXDomainRequestHandler:

package kbs.client;

import com.google.gwt.core.client.JavaScriptObject;

public interface IXDomainRequestHandler
{ public void handle(JavaScriptObject obj); }

src/kbs/client/Test.java:
package kbs.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JavaScriptObject;

import com.google.gwt.user.client.Window;

public class Test
implements EntryPoint, IXDomainRequestHandler
{
public void onModuleLoad()
{
CXDomainRequest.asyncCall

("http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Madonna&results=2&output=json&callback=",
this);
}

public native void handle(JavaScriptObject value)
/*-{
// Just to show something interesting came back.
alert("There are "+value.ResultSet.totalResultsAvailable+"
results");
}-*/;
}

bhi...@gmail.com

unread,
Apr 6, 2007, 6:36:33 PM4/6/07
to kbs, Google-We...@googlegroups.com
This code is working well for me, but i am getting crashes in
mshtml.dll when i 'reload' an app which does this call onload, in the
GWT Shell. This makes debugging painful since i can't reload.

If i commend out the head.appendChild(script) line, it doesn't crash,
but then it doesn't work either :)

Any ideas, or better code to do cross domain JSON?

thanks,

-Ben

On Aug 16 2006, 4:08 pm, "kbs" <kbsri...@gmail.com> wrote:
> I've been playing with some code to inject dynamic script tags and
> retrieve JSON data from arbitrary servers. Here are some notes and
> sample code, in the hopes that it will be useful to someone. Also
> looking for better ways and code to solve this problem.
>
> Some background:
> The cross domain JSON object retrieval method presumes a JSON service
> similar to the yahoo callback api. eg:
>

> http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?appid=Y...

> *http://developer.yahoo.com/common/json.html#callbackparam)

> handl...@kbs.client.IXDomainRequestHandler::handle(Lcom/google/gwt/core/client/JavaScriptObject;)(json);


> }
> script.setAttribute("type", "text/javascript");
> script.setAttribute("src", url+"__xdomcb["+id+"]");
> head.appendChild(script);
> }-*/;
>
> // A unique counter.
> private static int s_id = 0;
>
> }
>
> src/kbs/client/IXDomainRequestHandler:
>
> package kbs.client;
>
> import com.google.gwt.core.client.JavaScriptObject;
>
> public interface IXDomainRequestHandler
> { public void handle(JavaScriptObject obj); }
>
> src/kbs/client/Test.java:
> package kbs.client;
>
> import com.google.gwt.core.client.EntryPoint;
> import com.google.gwt.core.client.JavaScriptObject;
>
> import com.google.gwt.user.client.Window;
>
> public class Test
> implements EntryPoint, IXDomainRequestHandler
> {
> public void onModuleLoad()
> {
> CXDomainRequest.asyncCall
>

> ("http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?appid=Y...",

Reply all
Reply to author
Forward
0 new messages