我正在尝试编写一个库,让我通过Nashorn Javascript引擎执行JSON Logic规则。
我现在的问题是围绕我创建的JSObject包装器来处理从Java / Kotlin到脚本引擎的移动数据。
如果传递了一个数组,例如[true]
它被包装并且json-logic脚本将接收它,请看它是一个数组,并尝试运行以下代码:
if(Array.isArray(logic)) {
return logic.map(function(l) {
return jsonLogic.apply(l, data);
});
}
当调用.map
函数时,Nashorn会在我的对象上调用getMember("map")
,希望得到一个可以执行的函数。
这是我被困的地方。我无法找到任何正确的语法来为Nashorn提供一个方法或方法引用,可以将其作为其map函数的接收者调用。
代码可在此处找到:https://github.com/deinspanjer/json-logic-java
有一些基本的单元测试,包括展示问题的单元测试JavaJsonLogicTest.simpleApplyJEJO()
。
被破坏的代码行是com/jsonlogic/JSObjectWrappers.kt:97
。
我非常感谢你的帮助。
更新 根据接受的答案,这里是代码的工作Kotlin版本:
package com.jsonlogic
import jdk.nashorn.api.scripting.AbstractJSObject
import jdk.nashorn.api.scripting.JSObject
import java.util.function.Function
import javax.script.ScriptEngineManager
fun main(args: Array<String>) {
val m = ScriptEngineManager()
val e = m.getEngineByName("nashorn")
// The following JSObject wraps this list
val l = mutableListOf<Any>()
l.add("hello")
l.add("world")
l.add(true)
l.add(1)
val jsObj = object : AbstractJSObject() {
override fun getMember(name: String?): Any? {
if (name == "map") {
// return a functional interface object - nashorn will treat it like
// script function!
return Function { callback: JSObject ->
val res = l.map {
// call callback on each object and add the result to new list
callback.call(null, it)
}
// return fresh list as result of map (or this could be another wrapper)
res
}
} else {
// unknown property
return null
}
}
}
e.put("obj", jsObj)
// map each String to it's uppercase and print result of map
e.eval("print(obj.map(function(x) '\"'+x.toString()+'\"'))");
}
答案 0 :(得分:4)
JSObject.getMember可以返回任何脚本“callable”。这可能是另一个为isFunction或Java功能接口对象返回'true'的JSObject。这里有几个简单的Java示例程序:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// The following JSObject wraps this list
List<String> l = new ArrayList();
l.add("hello");
l.add("world");
JSObject jsObj = new AbstractJSObject() {
@Override
public Object getMember(String name) {
// return a "function" object for "map"
if (name.equals("map")) {
return new AbstractJSObject() {
@Override
public Object call(Object thiz, Object... args) {
// first argument is the callback passed from script
JSObject callable = (JSObject)args[0];
List<Object> res = new ArrayList<>();
for (Object obj : l) {
// call callback on each object and add the result to new list
res.add(callable.call(null, obj));
}
// return fresh list as result of map (or this could be another wrapper)
return res;
}
@Override
public boolean isFunction() { return true; }
};
} else {
// unknown property
return null;
}
}
};
e.put("obj", jsObj);
// map each String to it's uppercase and print result of map
e.eval("print(obj.map(function(x) x.toUpperCase()))");
}
}
上面的示例为“map”属性返回一个可调用的JSObject。返回的“函数”本身使用回调函数作为参数。所有脚本函数(和对象)都作为JSObject传递给Java代码,因此'map'代码将第一个参数强制转换为JSObject以调用脚本回调函数。
以上修改为使用功能界面的示例如下:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
import java.util.*;
import java.util.function.*;
public class Main2 {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// The following JSObject wraps this list
List<String> l = new ArrayList();
l.add("hello");
l.add("world");
JSObject jsObj = new AbstractJSObject() {
@Override
public Object getMember(String name) {
if (name.equals("map")) {
// return a functional interface object - nashorn will treat it like
// script function!
return (Function<JSObject, Object>)callback -> {
List<Object> res = new ArrayList<>();
for (Object obj : l) {
// call callback on each object and add the result to new list
res.add(callback.call(null, obj));
}
// return fresh list as result of map (or this could be another wrapper)
return res;
};
} else {
// unknown property
return null;
}
}
};
e.put("obj", jsObj);
// map each String to it's uppercase and print result of map
e.eval("print(obj.map(function(x) x.toUpperCase()))");
}
}
希望以上示例可以帮助您为您的场景提供Kotlin版本。