0

当前尝试使用互操作方法在 GWT 中对 Java ArrayList 对象进行字符串化,以调用本机 JSON.stringify(ArrayListobj)。这会以数组的形式生成底层数组列表内容的完美 JSON 表示。这甚至适用于看似更复杂的 Java 类对象,但在本例中,我将使用字符串来演示。首先在我的创作方面:

test = new ArrayList<>();
test.add("first");
test.add("second");
String jsonstr = JSON.stringify(test);

现在基于我的 JsInterop 代码,我返回一个对象(或对象数组,如下面的代码所示):

@JsType(isNative=true, namespace=GLOBAL)
public class JSON {
  public native static String stringify(Object obj);
  public native static Object[] parse(String obj);
}

到目前为止一切顺利,所有这些都完美运行,我得到的结果是 ArrayList 内容的字符串化 JSON 表示

"{"array_0":["first","second"]}"

然后通过解析器运行这个位:

ArrayList<String> returned = new ArrayList<>();
Object[] var = JSON.parse(jsonstr);

并且 var 是来自第一个 ArrayList 的基础数据的正确表示(当查看 web 浏览器执行暂停时)。问题是将 JSON 数组的 Object[] 转换回 Java ArrayList 对象。

我尝试使用 JSNI 代码来提取数组元素,实际上它在 Web 浏览器的控制台托盘中运行良好,但编译器试图智取我并重命名数组元素,因此我的 JSNI 代码无法触及它。

如果我的代码如上,并且我编写 JSNI 类似:

    public static native ArrayList objToList(ArrayList add,Object inVal) /*-{
         var length = inVal.array.length;
         for(var i = 0;i < length; i++){
             add.array[i] = inVal.array[i];
         }
         return add;
     }-*/;

然后编译器将重命名数组array_0,这样我的代码 inVal.array 就不再与我试图访问的数据相关联。

从我所做的所有测试来看,这是迄今为止从客户端软件中的一个位置到客户端软件中的另一个位置(没有服务器)获取相同 ArrayList 对象(保证在两个地方以相同方式定义)的最快方法这里涉及)通过字符串化。

但是关于如何在 GWT 中低级别操作 JavaScript 的信息充其量是缺乏的。

而且我已经尝试了 GWT-RPC 机制、GWT-Jackson、AutoBeans 的所有变体(如果它们支持具有多种原始类型的对象!)requestbuilder,你可以命名它。

不,在您提出建议之前,我对再次对字符串进行完整的 GWT-JSON 解析不感兴趣,当最初将数千条记录从服务器中拉出并将它们推送到 Java ArrayList 时,我已经这样做了。在 GWT 中解析 JSON 需要 200+mS,而浏览器 JSON 解析函数处理这个字符串大约需要 3mS。

4

1 回答 1

2

GWT 使用类型标记来跟踪类型并能够进行类转换安全。您绝不能将 stringify 与 Java 类一起使用,因为您将丢失这些类型标记,并且您将使用内部最小化/编码符号。因此,这就是 GWT 在内部处理所有这些类型的方式:

List<String> list = new ArrayList<>();
list.add("a"); list.add("b");
console.log("list", list);
console.log("array", list.toArray());
console.log("stringify", Global.JSON.stringify(list.toArray()));
console.log("parse", Global.JSON.parse(Global.JSON.stringify(list.toArray())));

控制台输出 'list' 包含混淆变量array_8_g$,这可能会改变,所以你永远不应该使用这种编码。结果array没问题,但你应该注意到它包含各种属性(typemarker、casteableTypeMap 和 __clazz),这些额外的属性用于使 java 强制转换工作,但不可枚举,因此不包含在下一个结果stringify中。这个stringify结果可以被解析为 a String[],但现在的结果parse不包括类型标记属性。因此,如果您立即将结果保存parseString[]变量中,它将正常工作。但是,如果您将其转换为Object并尝试将其转换回String[]它将失败。

在 jsinterop:base 依赖项中有 2 个实用程序 Js#cast 和 Js#uncheckedCast 在这些情况下很有帮助。如果数组有类型标记,则可以使用 Js#cast(此实用程序与标准 java 强制转换相同),否则必须使用 Js#uncheckedCast。

在这个例子中,第一行会成功,第二行会因为类转换异常而失败:

console.log("uncheck", Js.<String[]>uncheckedCast(JSON.parse(JSON.stringify(list.toArray())))[0]);
console.log("check", Js.<String[]>cast(JSON.parse(JSON.stringify(list.toArray())))[0]);

你真的应该尽量避免将原生 JS 代码与 Java 混合。如果你需要这样做,那么你必须了解GWT如何处理类型的内部机制,你必须了解JSNI但尽可能少使用它,最后了解JsInterop是如何工作的,并使用它来访问原生JS代码或暴露Java代码到 JS 世界。

于 2019-02-07T12:50:54.913 回答