GraalVM – 将Java复杂对象暴露给JavaScript

huangapple go评论85阅读模式
英文:

GraalVM - Exposing Java complex objects to JavaScript

问题

使用GraalVM,将Java对象暴露给JavaScript,我使用ProxyObject来进行包装。为此,我使用ProxyObject.fromMap方法,如下所示:

ProxyObject exposedObject = ProxyObject.fromMap(objectMapper.convertValue(javaObject, Map.class));

这里,javaObjectObject类的对象,可以是任意复杂性。这个方法适用于javaObject的直接成员,但是当成员本身是复杂对象时就不适用了。例如,如果javaObject的成员之一恰好是一个Map,像这样:

final Map<String, Object> source = new HashMap<>();
source.put("id", "1234567890");
final Map<String, Object> sourceComponent = ImmutableMap.of("key", "value");
source.put("complex", sourceComponent);

// 假设source是任意对象
ProxyObject exposedObject = ProxyObject.fromMap(objectMapper.convertValue(source, Map.class));

// 或者知道source实际上是一个map
ProxyObject exposedObject = ProxyObject.fromMap(source);

当在JavaScript中访问exposedObject时,会出现以下情况:

exposedObject; // 返回 {complex: JavaObject[com.google.common.collect.SingletonImmutableBiMap], id: "1234567890"}

exposedObject.id; // 返回 01234567890

exposedObject.complex; // 返回 {key=value}

exposedObject.complex.key; // 返回 undefined

所以我的问题是如何完全将一个任意复杂和深层次的Java对象暴露给JavaScript。我们是否需要递归地遍历所有成员并将它们包装成ProxyObject?或者是否有一种支持的开箱即用的方法来实现这一点?

另外,请让我知道是否需要更改我的方法。

英文:

Using GraalVM, to expose Java objects to JavaScript, I am using ProxyObject to wrap them. For this purpose, I am using ProxyObject.fromMap method like the following:

ProxyObject exposedObject = ProxyObject.fromMap(objectMapper.convertValue(javaObject, Map.class));

Here, the javaObject is of Object class and can be arbitrarily complex. This method works for immediate members of javaObject, but not when the members are complex objects themselves. For example, if one of the members of javaObject happens to be a Map, like:

        final Map&lt;String, Object&gt; source = new HashMap&lt;&gt;();
        source.put(&quot;id&quot;, &quot;1234567890&quot;);
        final Map&lt;String, Object&gt; sourceComponent = ImmutableMap.of(&quot;key&quot;, &quot;value&quot;);
        source.put(&quot;complex&quot;, sourceComponent);

        // assuming the source is any object   
        ProxyObject exposedObject = ProxyObject.fromMap(objectMapper.convertValue(source, Map.class));

        // or knowing that source is in fact a map
        ProxyObject exposedObject = ProxyObject.fromMap(source);

this is what happens when the exposedObject is accessed in JavaScript:

exposedObject; // returns {complex: JavaObject[com.google.common.collect.SingletonImmutableBiMap], id: &quot;1234567890&quot;}

exposedObject.id; // returns 01234567890

exposedObject.complex; // returns {key=value}

exposedObject.complex.key; // returns undefined

So my question is how we can fully expose an arbitrarily complex and deep java object to javascript. Do we have to go through all members recursively and wrap them into ProxyObjects? Or is there a supported out-of-the-box method of achieving this?

Also, please let me know if my approach needs to change.

答案1

得分: 1

以下是翻译好的部分:

如 ProxyObject 的 javadoc [1] 所述:“要实现的接口,用于模仿包含成员的宿主语言对象。”。这意味着,如果您希望将 Java 对象用作 JavaScript 中的对象,就像它是 JavaScript 本地对象一样,它需要是一个 ProxyObject。

另一方面,正如网站文档 [2] 所示,传递到 JavaScript 中的 Java 对象仍然可以被用作 Java 对象(即它们默认情况下不会模仿 JS 对象)。这意味着您可以访问字段,调用方法等。
网站文档中展示了一个示例:

public static class MyClass {
    public int id = 42;
    public String text = "42";
    public int[] arr = new int[]{1, 42, 3};
    public Callable<Integer> ret42 = () -> 42;
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                               .build()) {
        context.getBindings("js").putMember("javaObj", new MyClass());
        boolean valid = context.eval("js",
               "javaObj.id         == 42"          +
               " && javaObj.text       == '42'"        +
               " && javaObj.arr[1]     == 42"          +
               " && javaObj.ret42()    == 42")
           .asBoolean();
        assert valid == true;
    }
}

[1] https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/proxy/ProxyObject.html

[2] https://www.graalvm.org/reference-manual/embed-languages/

英文:

As the javadoc for ProxyObject [1] says "Interface to be implemented to mimic guest language objects that contain members.". This means that if you want the Java object to be used in JavaScript as if it was native to JavaScript it needs to be a ProxyObject.

On the other hand, as the website docs [2] show, Java objects passed into JavaScript can still be used as Java objects (i.e. they don't mimic JS objects by default). This means you can access fields, invoke methods, etc.
The website docs show an example:

public static class MyClass {
    public int               id    = 42;
    public String            text  = &quot;42&quot;;
    public int[]             arr   = new int[]{1, 42, 3};
    public Callable&lt;Integer&gt; ret42 = () -&gt; 42;
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        context.getBindings(&quot;js&quot;).putMember(&quot;javaObj&quot;, new MyClass());
        boolean valid = context.eval(&quot;js&quot;,
               &quot;    javaObj.id         == 42&quot;          +
               &quot; &amp;&amp; javaObj.text       == &#39;42&#39;&quot;        +
               &quot; &amp;&amp; javaObj.arr[1]     == 42&quot;          +
               &quot; &amp;&amp; javaObj.ret42()    == 42&quot;)
           .asBoolean();
        assert valid == true;
    }
}

[1] https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/proxy/ProxyObject.html

[2] https://www.graalvm.org/reference-manual/embed-languages/

huangapple
  • 本文由 发表于 2020年10月2日 05:05:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/64163189.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定