我如何在其中传递适当的方法引用,以便Nashorn可以执行它? [英] How can I pass a proper method reference in so Nashorn can execute it?

查看:90
本文介绍了我如何在其中传递适当的方法引用,以便Nashorn可以执行它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个库,该库将允许我通过Nashorn Javascript引擎执行JSON逻辑规则.

I am trying to write a library that will let me execute JSON Logic rules via the Nashorn Javascript engine.

我现在的问题特别是围绕我创建的JSObject包装器,该包装器用于处理将数据从Java/Kotlin移动到脚本引擎中.

My problem right now is specifically around the JSObject wrapper I've created to handle moving data from Java/Kotlin into the scripting engine.

如果传入一个数组,例如[true],它将被包装并且json-logic脚本将接收它,请查看它是一个数组,然后尝试运行以下代码:

If an array is passed in such as [true] it is wrapped and the json-logic script will receive it, see that it is an array, and attempt to run the following bit of code:

if(Array.isArray(logic)) {
  return logic.map(function(l) {
    return jsonLogic.apply(l, data);
  });
}

当调用.map函数时,Nashorn将在我的对象上调用getMember("map"),期望它返回一个可以执行的函数.

When the .map function is called, Nashorn will call getMember("map") on my object expecting to get a function back that it can execute.

这就是我被困住的地方.我无法找到任何合适的语法来给Nashorn提供方法或方法引用,该方法或方法引用可以由Nashorn作为其map函数的接收者来调用.

This is where I am stuck. I have not been able to find any sort of proper syntax to give Nashorn a method or method reference that can be invoked by it as the receiver of its map function.

代码可在此处获取: https://github.com/deinspanjer/json-logic -java 有一些基本的单元测试,包括表现出问题的一个,JavaJsonLogicTest.simpleApplyJEJO(). 损坏的代码行是com/jsonlogic/JSObjectWrappers.kt:97.

The code is available here: https://github.com/deinspanjer/json-logic-java There are some basic unit tests including the one that exhibits the problem, JavaJsonLogicTest.simpleApplyJEJO(). The line of code that is broken is com/jsonlogic/JSObjectWrappers.kt:97.

非常感谢您的帮助.

更新: 根据已接受的答案,这是该代码的有效Kotlin版本:

UPDATE: Based on the accepted answer, here is the working Kotlin version of the code:

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()+'\"'))");
}

推荐答案

JSObject.getMember可以返回任何可调用"脚本.那可能是另一个为isFunction或Java功能接口对象返回"true"的JSObject.这里有几个简单的Java示例程序:

JSObject.getMember can return any script "callable". That could be another JSObject that returns 'true' for isFunction or a Java functional interface object. Couple of simple Java sample programs here:

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.返回的函数"本身使用回调函数作为参数.所有脚本函数(和对象)都作为JSObjects传递给Java代码,因此映射"代码将第一个参数强制转换为JSObject来调用脚本回调函数.

The above example returns a callable JSObject for "map" property. The returned "function" itself uses a callback function as argument. All script functions (and objects) are passed as JSObjects to Java code and so 'map' code casts first argument to JSObject to invoke the script callback function.

上面修改为使用功能接口的示例如下:

The above sample modified to use a functional interface is as follows:

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版本.

Hope the above samples will help you in coming up with Kotlin version for your scenario.

这篇关于我如何在其中传递适当的方法引用,以便Nashorn可以执行它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆