开发者

Getting and using remote JSON data

I'm working on a little app and using GWT to build it. I just tried making a request to a remote server which will return a response as JSON. I've tried using the overlay types concept but I couldn't get it working. I've been changing the code around so its a bit off from where the Google GWT tutorials left.

JavaScriptObject json;
    public JavaScriptObject executeQuery(String query) {
        String url = "http://api.domain.com?client_id=xxxx&query=";
        RequestBuilder builder = new Req开发者_运维问答uestBuilder(RequestBuilder.GET,
                URL.encode(url + query));
        try {
            @SuppressWarnings("unused")
            Request request = builder.sendRequest(null, new RequestCallback() {
                public void onError(Request request, Throwable exception) {
                    // violation, etc.)
                }

                public void onResponseReceived(Request request,
                        Response response) {
                    if (200 == response.getStatusCode()) {
                        // Process the response in response.getText()
                        json =parseJson(response.getText());
                    } else {

                    }
                }
            });
        } catch (RequestException e) {
            // Couldn't connect to server
        }
        return json;
    }

    public static native JavaScriptObject parseJson(String jsonStr) /*-{
        return eval(jsonStr );
        ;
    }-*/;

In the chrome's debugger I get umbrellaexception, unable to see the stack trace and GWT debugger dies with NoSuchMethodError... Any ideas, pointers?


You may have a look to GWT AutoBean framework.

AutoBean allow you to serialize and deserialize JSON string from and to Plain Old Java Object.

For me this framework became essential :

  • Code is cleaner than with JSNI objects (JavaScript Native Interface)
  • No dependancy with Framework not supported by Google (like RestyGWT)

You just define interfaces with getters and setters :

// Declare any bean-like interface with matching getters and setters, 
// no base type is necessary
interface Person {
  Address getAddress();
  String getName();
  void setName(String name):
  void setAddress(Address a);
}

interface Address {
  String getZipcode();
  void setZipcode(String zipCode);
}

Later you can serialize or deserialize JSON String using a factory (See documentation) :

// (...)

String serializeToJson(Person person) {
  // Retrieve the AutoBean controller
  AutoBean<Person> bean = AutoBeanUtils.getAutoBean(person);

  return AutoBeanCodex.encode(bean).getPayload();
}

Person deserializeFromJson(String json) {
  AutoBean<Person> bean = AutoBeanCodex.decode(myFactory, Person.class, json);
  return bean.as();
}

// (...)

First post on Stack Overflow (!) : I hope this help :)


  1. Use JsonUtils#safeEval() to evaluate the JSON string instead of calling eval() directly.
  2. More importantly, don't try to pass the result of an asynchronous call (like RequestBuilder#sendRequest() back to a caller using return - use a callback:

    public void executeQuery(String query,
                             final AsyncCallback<JavaScriptObject> callback)
    {
      ...
      try {
        builder.sendRequest(null, new RequestCallback() {
          public void onError(Request request, Throwable caught) {
            callback.onFailure(caught);
          }
    
          public void onResponseReceived(Request request, Response response) {
            if (Response.SC_OK == response.getStatusCode()) {
              try {
                callback.onSuccess(JsonUtils.safeEval(response.getText()));
              } catch (IllegalArgumentException iax) {
                callback.onFailure(iax);
              }
            } else {
              // Better to use a typed exception here to indicate the specific
              // cause of the failure.
              callback.onFailure(new Exception("Bad return code."));
            }
          }
        });
      } catch (RequestException e) {
        callback.onFailure(e);
      }
    }
    


Generally, the workflow you're describing consists of four steps:

  1. Make the request
  2. Receive the JSON text
  3. Parse the JSON in JavaScript objects
  4. Describe these JavaScript objects using an overlay type

It sounds like you've already got steps 1 and 2 working properly.

Parse the JSON

JSONParser.parseStrict will do nicely. You'll be returned a JSONValue object.

This will allow you to avoid using your custom native method and will also make sure that it prevents arbitrary code execution while parsing the JSON. If your JSON payload is trusted and you want raw speed, use JSONParser.parseLenient. In either case, you need not write your own parser method.

Let's say that you're expecting the following JSON:

{
  "name": "Bob Jones",
  "occupations": [
    "Igloo renovations contractor",
    "Cesium clock cleaner"
  ]
}

Since you know that the JSON describes an object, you can tell the JSONValue that you're expecting to get a JavaScriptObject.

String jsonText = makeRequestAndGetJsonText(); // assume you've already made request
JSONValue jsonValue = JSONParser.parseStrict(jsonText);
JSONObject jsonObject = jsonValue.isObject(); // assert that this is an object
if (jsonObject == null) {
  // uh oh, it wasn't an object after
  // do error handling here
  throw new RuntimeException("JSON payload did not describe an object");
}

Describe as an overlay type

Now that you know that your JSON describes an object, you can get that object and describe it in terms of a JavaScript class. Say you have this overlay type:

class Person {
  String getName() /*-{
    return this.name;
  }-*/;
  JsArray getOccupations() /*-{
    return this.occupations;
  }-*/;
}

You can make your new JavaScript object conform to this Java class by doing a cast:

Person person = jsonObject.getJavaScriptObject().cast();
String name = person.getName(); // name is "Bob Jones"


Using eval is generally dangerous, and can result in all kinds of strange behavior, if the server returns invalid JSON (note, that it's necessary, that the JSON top element is an array, if you simply use eval(jsonStr)!). So I'd make the server return a very simple result like

[ "hello" ]

and see, if the error still occurs, or if you can get a better stack trace.

Note: I assume, that the server is reachable under the same URL + port + protocol as your GWT host page (otherwise, RequestBuilder wouldn't work anyway due to Same Origin Policy.)


You actually don't need to parse the JSON, you can use native JSNI objects (JavaScript Native Interface).

Here's an example I pulled from a recent project doing basically the same thing you're doing:

public class Person extends JavaScriptObject{
    // Overlay types always have protected, zero argument constructors.
    protected Person(){}

    // JSNI methods to get stock data
    public final native String getName() /*-{ return this.name; }-*/;
    public final native String getOccupation() /*-{ return this.occupation; }-*/;

    // Non-JSNI methods below
}

and then to retrieve it like so:

/**
   * Convert the string of JSON into JavaScript object.
   * 
   */
  private final native JsArray<Person> asArrayOfPollData(String json) /*-{
    return eval(json);
  }-*/;

private void retrievePeopleList(){

      errorMsgLabel.setVisible(false);

      String url = JSON_URL;
      url = URL.encode(url);

      RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);

      try{
          @SuppressWarnings("unused")
          Request request = builder.sendRequest(null, new RequestCallback() {
            @Override
            public void onResponseReceived(Request req, Response resp) {
                if(resp.getStatusCode() == 200){
                    JsArray<Person> jsonPeople = asArrayOfPeopleData(resp.getText()); 
                    populatePeopleTable(people);
                }
                else{
                    displayError("Couldn't retrieve JSON (" + resp.getStatusText() + ")");
                }
            }

            @Override
            public void onError(Request req, Throwable arg1) {
                System.out.println("couldn't retrieve JSON");
                displayError("Couldn't retrieve JSON");
            }
        });
      } catch(RequestException e) {
          System.out.println("couldn't retrieve JSON");
          displayError("Couldn't retrieve JSON");
      }
  }

So essentially you're casting the response as an array of JSON Objects. Good stuff.

More info here: http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜