I have a Java array, say, Object[]
, which I need to pass to the JS execution environment, which is ScriptEngine
.
I cannot simply put it as a property in the following way:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
Object[] array = {1, 2, 3};
engine.put("prop", array);
because in the JS environment expression Array.isArray(prop)
will be evaluated to false
, whereas I need it to be true
.
jdk.nashorn.internal.objects.NativeArray
constructors are closed, which means you cannot instantiate NativeArray
explicitly.
One can, however, use jdk.nashorn.internal.objects.Global.instance().wrapAsObject
to convert Java array Object[]
to NativeArray
, and the resultant object would be recognized as a JS array, i.e. Array.isArray
would return true for this object.
Although this gives the desired result, using classes from internal
package is not a very good idea, and even worse idea if you are using Java 9.
Thus, I am wondering, is there a better way to provide Java object to the JS execution environment assuming I cannot change the JS source so that the object is recognized a true JS array, i.e. Array.isArray
returns true?
You can create a native array through javascript, and then convert the corresponding ScriptObjectMirror
to a List<Object>
, it looks like the list will use the native array as underlying storage:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
ScriptObjectMirror jsArray = (ScriptObjectMirror) engine.eval("var arr = []; arr");
@SuppressWarnings("unchecked")
List<Object> ls = jsArray.to(List.class);
ls.add(1);
ls.add(2);
ls.add(3);
System.out.println(ls); // [1, 2, 3]
engine.eval("print(arr)"); // 1,2,3
engine.eval("print(Array.isArray(arr))"); // true
You can then use this list on the Java side.