Nashorn ScriptObjectMirror JS -> Java 类型转换

9
当我使用Nashorn ScriptObjectMirror.get()访问JavaScript对象的成员变量时,返回的对象类型似乎是在运行时确定的。例如,如果值适合Java int,则get()似乎返回Java Integer。如果值无法适应int,则get()似乎返回Java Long,依此类推。
现在,我使用instanceof检查类型并将值转换为long。
有没有更方便的方法在Java中获取成员的值而不会丢失,并且不需要检查类型?也许Nashorn总是可以给我一个Java Double,在成员不是数字的情况下抛出错误。
我想象这可能是一个相当狭窄的情况,可能不应该由Nashorn处理...
例子:
package com.tangotangolima.test.nashorn_types;

import jdk.nashorn.api.scripting.ScriptObjectMirror;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.StringReader;

public class Main {

    public static void main(String[] args) throws ScriptException {
        final ScriptEngineManager mgr = new ScriptEngineManager();
        final ScriptEngine js = mgr.getEngineByName("nashorn");

        final String script = "" +
                "var p = 1;" +
                "var q = " + (Integer.MAX_VALUE + 1L) + ";" +
                "var r = {" +
                "s: 1," +
                "t: " + (Integer.MAX_VALUE + 1L) +
                " };";

        js.eval(new StringReader(script));

        say(js.get("p").getClass().getName());      // -> java.lang.Integer
        say(js.get("q").getClass().getName());      // -> java.lang.Long

        final ScriptObjectMirror r = (ScriptObjectMirror) js.get("r");

        say(r.get("s").getClass().getName());       // -> java.lang.Integer
        say(r.get("t").getClass().getName());       // -> java.lang.Long
    }

    static void say(String s) {
        System.out.println(s);
    }
}
3个回答

13

这段代码可以将ScriptObjectMirror JS转换为Java

private static Object convertIntoJavaObject(Object scriptObj) {
    if (scriptObj instanceof ScriptObjectMirror) {
        ScriptObjectMirror scriptObjectMirror = (ScriptObjectMirror) scriptObj;
        if (scriptObjectMirror.isArray()) {
            List<Object> list = Lists.newArrayList();
            for (Map.Entry<String, Object> entry : scriptObjectMirror.entrySet()) {
                list.add(convertIntoJavaObject(entry.getValue()));
            }
            return list;
        } else {
            Map<String, Object> map = Maps.newHashMap();
            for (Map.Entry<String, Object> entry : scriptObjectMirror.entrySet()) {
                map.put(entry.getKey(), convertIntoJavaObject(entry.getValue()));
            }
            return map;
        }
    } else {
        return scriptObj;
    }
}

public static void main(String[] args) throws ScriptException, NoSuchMethodException {
    final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    engine.eval("function objProvider(){return {a:1, b:'2','c': true,'d': {'e':[],'f':['1',{'g':45}]}};}");
    final Object scriptObj = ((Invocable) engine).invokeFunction("objProvider");

    Object javaObj = convertIntoJavaObject(scriptObj);
    System.out.println(javaObj);
    //{a=1, b=2, c=true, d={e=[], f=[1, {g=45}]}}
}

https://dev59.com/HnrZa4cB1Zd3GeqPyh4H - Andrew
很遗憾,此答案无法运行,因为它无法进行转换...请参阅上面的链接获取更多信息;但可以在JS中直接构建Java集合。 - Andrew

2
推荐的方法是从Java代码中检查“instanceof java.lang.Number”——如果您期望JavaScript“number”值。一旦转换为Number,您可以通过调用intValue、longValue等方法将其转换为int、long、double等类型。请注意保留HTML标签。

2

我非常喜欢Igor的方法。以下是他的convertToJavaObject()代码,作为一个完整的程序,仅使用标准的Java,并且除了包括一个toJava()之外,还包括一个相反的toJavascript()

import jdk.nashorn.api.scripting.ScriptObjectMirror;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Main {

  @SuppressWarnings("removal")
  private static Object toJava(Object jsObj) {
    if (jsObj instanceof ScriptObjectMirror) {
      var jsObjectMirror = (ScriptObjectMirror) jsObj;
      if (jsObjectMirror.isArray()) {
        var list = new ArrayList<>();
        for (Map.Entry<String, Object> entry : jsObjectMirror.entrySet()) {
          list.add(toJava(entry.getValue()));
        }
        return list;
      } else {
        var map = new HashMap<String, Object>();
        for (Map.Entry<String, Object> entry : jsObjectMirror.entrySet()) {
          map.put(entry.getKey(), toJava(entry.getValue()));
        }
        return map;
      }
    } else {
      return jsObj;
    }
  }

  public static void main(String[] args) throws ScriptException, NoSuchMethodException {
    var code = String.join("\n",
"function objProvider() {",
"  return {a:1, b:'2','c': true,'d': {'e':[],'f':['1',{'g':45}]}}",
"}",
"function toJavascript (jObj) {",
"  if (jObj instanceof java.util.List) {",
"    var l = []; for each (var item in jObj) {",
"      l.push(toJavascript(item));",
"    }",
"    return l;",
"  }",
"  if (jObj instanceof java.util.Map) {",
"    var m = {}; for each (var key in jObj.keySet()) {",
"      m[key] = toJavascript(jObj.get(key));",
"    }",
"    return m;",
"  }",
"  return jObj;",
"}"
);
    var engine = new ScriptEngineManager().getEngineByName("nashorn");
    engine.eval(code);
    var jsObj = ((Invocable) engine).invokeFunction("objProvider");
    var formatted = ((Invocable) engine).invokeMethod(engine.eval("JSON"), "stringify", jsObj);
    System.out.println("JSON.stringify(jsObj): " + formatted);
    var javaObj = toJava(jsObj);
    System.out.println("javaObj: " + javaObj);
    //{a=1, b=2, c=true, d={e=[], f=[1, {g=45}]}}
    var newJsObj = ((Invocable) engine).invokeFunction("toJavascript", javaObj);
    formatted = ((Invocable) engine).invokeMethod(engine.eval("JSON"), "stringify", newJsObj);
    System.out.println("JSON.stringify(newJsObj): " + formatted);
    // just to show this doesn't work without conversion
    formatted = ((Invocable) engine).invokeMethod(engine.eval("JSON"), "stringify", javaObj);
    System.out.println("JSON.stringify(javaObj):  " + formatted);
  }
}

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