【问题标题】:Nashorn ScriptObjectMirror JS -> Java type conversionNashorn ScriptObjectMirror JS -> Java 类型转换
【发布时间】:2015-11-10 14:33:13
【问题描述】:

当我使用 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);
    }
}

【问题讨论】:

    标签: java java-8 nashorn scriptengine


    【解决方案1】:

    这段代码可以做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}]}}
    }
    

    【讨论】:

    • 不幸的是,这个答案不起作用,因为它不能被投射......有关更多信息,请参见上面的链接;不过可以直接在 JS 中构造 Java 集合。
    【解决方案2】:

    推荐的方法是检查 java 代码中的“instanceof java.lang.Number”——如果您期望 JavaScript “number”值。转换为 Number 后,可以通过调用 intValue、longValue 等方法转换为 int、long、double。

    【讨论】:

      【解决方案3】:

      我真的很喜欢Igor's 的方法。这是他的 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);
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-02-03
        • 2018-04-07
        • 2015-11-09
        • 2018-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多