Java:ScriptEngine中返回JavaScript对象

15

我正在尝试使用ScriptEngine类在Java中评估JavaScript。这是我想要实现的简短示例:

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;

public class Test {
    public static void main(String[] args) {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); //Creates a ScriptEngine
        Object obj = engine.eval("var obj = { value: 1 }; return obj; "); // Evals the creation of a simple object
        System.out.println(obj.value); // I get an invalid token error when trying to print a property of the object
    }
}

我相信这个应该能够运行……但是我却束手无策,需要任何帮助。


如果删除 return obj;,它还能正常工作吗?返回语句应该在函数中使用,但我不知道该类如何评估JS代码。 - user1106925
1
我也不太确定它是如何工作的;我只是看了几个例子和文档。当省略时,我会得到相同的错误。 - hjk321
可能是因为var语句不会返回对象本身,只有用括号包裹的对象才能生效。这些括号对于一个有效的程序来说是必要的。 - user1106925
@squint OP出现了编译错误(在println()语句的注释中提到的“无效令牌错误”),而不是运行时错误。请参阅我的答案以了解原因。 - Andreas
@Andreas:我知道,看看我的第一条评论。我只是想说var obj = { value: 1 };{value: 1};两者都不太可能起作用,因为单独的任何一个语句都无法产生一个对象,而({value: 1})则可以。 - user1106925
1个回答

14

注意:以下内容是针对Java 8,使用Nashorn引擎。

首先,为了使代码编译通过,请从println()语句中删除.valueobj被声明为Object类型,而Object没有value字段。

一旦您这样做了,运行代码时会得到以下异常:

Exception in thread "main" javax.script.ScriptException: <eval>:1:25 Invalid return statement
var obj = { value: 1 };  return obj; 
                         ^ in <eval> at line number 1 at column number 25

这是因为你没有一个函数,所以你不能调用return。脚本的返回值是最后一个表达式的值,所以只需说obj

现在它将运行并打印[object Object]。如果要查看您得到的对象的类型,请更改为println(obj.getClass().getName())。这将打印jdk.nashorn.api.scripting.ScriptObjectMirror。我已经链接到了您方便使用的javadoc文档。

ScriptObjectMirror实现Bindings,而Bindings又实现了Map <String, Object>,因此您可以调用get("value")

工作代码如下:

import javax.script.*;

public class Test {
    public static void main(String[] args) throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        Bindings obj = (Bindings)engine.eval("var obj = { value: 1 };  obj; ");
        Integer value = (Integer)obj.get("value");
        System.out.println(value); // prints: 1
    }
}

更新

整个重点是创建一个带有函数的对象,这个引擎是否支持?没有 Function 对象。

以下是如何实现的示例:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import jdk.nashorn.api.scripting.ScriptObjectMirror;

public class Test {
    public static void main(String[] args) throws Exception {
        String script = "var f = {\n" +
                        "  value: 0,\n" +
                        "  add: function(n) {\n" +
                        "    this.value += n;\n" +
                        "    return this.value;\n" +
                        "  }\n" +
                        "};\n" +
                        "f; // return object to Java\n";
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
        ScriptObjectMirror obj = (ScriptObjectMirror)engine.eval(script);
        System.out.println("obj.value = " + obj.getMember("value"));
        System.out.println("obj.add(5): " + obj.callMember("add", 5));
        System.out.println("obj.add(-3): " + obj.callMember("add", -3));
        System.out.println("obj.value = " + obj.getMember("value"));
    }
}

输出

obj.value = 0
obj.add(5): 5.0
obj.add(-3): 2.0
obj.value = 2.0

main() 中添加 throws ScriptException - Andreas
我知道这个问题已经有答案了,但是我似乎不能使用相同的代码来处理字符串。另外,在JavaScript中是否可以设置函数的值,然后在Java中执行该函数?这就是整个目的,它是为了创建动态函数。 - hjk321
是的,只需按照我所描述的做即可。打印返回对象的类名,以便您了解所获得的内容。然后检查Javadoc以了解如何使用它。 - Andreas
@Adam,假设Bindings没有必要的getMember(...)callMember(...)方法,你会怎么做呢?请注意,ScriptObjectMirror实现了Bindings - Andreas
抱歉,我误将ScriptObjectMirror视为内部JDK类,因为Eclipse将其隐藏了。显然它不是。在一个更简单的用例中,我使用get()而不是getMember()来访问变量。 - Adam
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接