Some background:
The cross domain JSON object retrieval method presumes a JSON service
similar to the yahoo callback api. eg:
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
public native void handle(JavaScriptObject value)
/*-{
// Just to show something interesting came back.
alert("There are "+value.ResultSet.totalResultsAvailable+"
results");
}-*/;
}
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...",