英文:
GraalVM - Exposing Java complex objects to JavaScript
问题
使用GraalVM,将Java对象暴露给JavaScript,我使用ProxyObject
来进行包装。为此,我使用ProxyObject.fromMap
方法,如下所示:
ProxyObject exposedObject = ProxyObject.fromMap(objectMapper.convertValue(javaObject, Map.class));
这里,javaObject
是Object
类的对象,可以是任意复杂性。这个方法适用于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<String, Object> source = new HashMap<>();
source.put("id", "1234567890");
final Map<String, Object> sourceComponent = ImmutableMap.of("key", "value");
source.put("complex", 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: "1234567890"}
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 ProxyObject
s? 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 = "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/
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论