When I access a JavaScript object's member variable using Nashorn ScriptObjectMirror.get(), the returned object's type seems to be determined at runtime. For example, if the value fits in a Java int, get() seems to return a Java Integer. If the value won't fit in an int, get() seems to return a Java Long, and so on.
Right now, I use instanceof to check the type and convert the value to a long.
Is there a more convenient way of getting a member's value without loss and without checking the type in Java? Perhaps Nashorn could always give me a Java Double, throwing an error in case the member's not numeric.
I can imagine this is a rather narrow case that probably shouldn't be handled by Nashorn...
Example:
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);
}
}
This code can do ScriptObjectMirror JS -> Java conversion
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}]}}
}
Recommended approach is to check "instanceof java.lang.Number" from java code -- if you expect JavaScript "number" value. Once casted to Number, you can convert to int, long, double by calling methods such as intValue, longValue etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With