3

我正在开发一个小应用程序并使用 GWT 来构建它。我刚刚尝试向远程服务器发出请求,该服务器将以 JSON 形式返回响应。我试过使用覆盖类型的概念,但我无法让它工作。我一直在更改代码,因此它与 Google GWT 教程的位置有点偏离。

JavaScriptObject json;
    public JavaScriptObject executeQuery(String query) {
        String url = "http://api.domain.com?client_id=xxxx&query=";
        RequestBuilder builder = new RequestBuilder(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 );
        ;
    }-*/;

在 chrome 的调试器中,我得到了雨伞异常,无法看到堆栈跟踪,并且 GWT 调试器因 NoSuchMethodError 而死...任何想法,指针?

4

5 回答 5

13

您可以看看GWT AutoBean 框架

AutoBean 允许您从普通旧 Java 对象序列化和反序列化 JSON 字符串。

对我来说,这个框架变得至关重要:

  • 代码比 JSNI 对象更干净(JavaScript 原生接口)
  • 不依赖于 Google 不支持的框架(如 RestyGWT)

您只需使用 getter 和 setter 定义接口:

// 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);
}

稍后您可以使用工厂序列化或反序列化 JSON 字符串(请参阅文档):

// (...)

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();
}

// (...)

Stack Overflow 上的第一篇文章(!):我希望这有帮助 :)

于 2011-04-02T17:03:44.073 回答
5
  1. 用于JsonUtils#safeEval()评估 JSON 字符串,而不是eval()直接调用。
  2. 更重要的是,不要尝试将异步调用的结果传递给调用者(例如RequestBuilder#sendRequest()使用return- 使用回调:

    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);
      }
    }
    
于 2011-04-01T21:54:30.757 回答
5

通常,您描述的工作流程包括四个步骤:

  1. 提出请求
  2. 接收 JSON 文本
  3. 解析 JavaScript 对象中的 JSON
  4. 使用覆盖类型描述这些 JavaScript 对象

听起来您已经使第 1 步和第 2 步正常工作。

解析 JSON

JSONParser.parseStrict会做得很好。您将获得一个JSONValue对象。

这将允许您避免使用自定义本机方法,并确保它在解析 JSON 时防止任意代码执行。如果您的 JSON 有效负载是可信的并且您想要原始速度,请使用JSONParser.parseLenient. 无论哪种情况,您都不需要编写自己的解析器方法。

假设您期待以下 JSON:

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

由于您知道 JSON 描述了一个对象,因此您可以JSONValue告诉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");
}

描述为覆盖类型

现在您知道您的 JSON 描述了一个对象,您可以获取该对象并根据 JavaScript 类对其进行描述。假设您有这种覆盖类型:

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

您可以通过强制转换使您的新 JavaScript 对象符合这个 Java 类:

Person person = jsonObject.getJavaScriptObject().cast();
String name = person.getName(); // name is "Bob Jones"
于 2011-04-01T22:18:06.797 回答
0

使用eval通常是危险的,并且可能导致各种奇怪的行为,如果服务器返回无效的 JSON(请注意,如果您简单地使用,那么 JSON 顶部元素是一个数组是必要的eval(jsonStr)!)。所以我会让服务器返回一个非常简单的结果,比如

[ "hello" ]

看看,如果错误仍然存​​在,或者你是否可以获得更好的堆栈跟踪。

注意:我假设服务器可以在与您的 GWT 主机页面相同的 URL + 端口 + 协议下访问(否则,由于同源策略,RequestBuilder 无论如何都不会工作。)

于 2011-04-01T21:32:56.947 回答
0

你实际上不需要解析 JSON,你可以使用原生的 JSNI 对象(JavaScript Native Interface)。

这是我从最近的一个项目中提取的一个示例,它与您正在做的事情基本相同:

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
}

然后像这样检索它:

/**
   * 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");
      }
  }

因此,本质上您将响应转换为 JSON 对象数组。好东西。

更多信息在这里:http ://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html

于 2011-04-01T22:43:55.300 回答