【问题标题】:Looking for JEXL Filter feature寻找 JEXL 过滤器功能
【发布时间】:2018-09-30 17:08:38
【问题描述】:

我知道,我可以在JEXL 中做几件事,但是在里面找不到Filter 功能,这确实很有用。

我该怎么做

 var x=[{a:11,b=5},{a:1,b=15},{a:12,b=25},{a:4,b=35},{a:7,b=45}];

 return x[.a>10].b; // Which filters to {a:11,b=5} & {a:12,b=25}
                   // & hence returns [5,25]

【问题讨论】:

    标签: java filtering jexl


    【解决方案1】:

    首先,您的语法不是有效的 JEXL。我假设你的意思是:

    var x = [{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}];
    

    由于您可以在 JEXL 脚本中对任何对象调用任何 Java 方法,因此您(至少理论上)拥有对 Java Stream API 的完全访问权限。

    但是,Stream API 不能直接从原始数组中获得,我们不能不费吹灰之力就直接调用 Arrays.stream(x);。解决这个问题的最简单方法是创建一个集合:

    var x = {{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}};
    

    现在我们可以简单地调用stream() 并从那里开始工作:

    x.stream();
    

    我们现在想要的是这样的:

    x.stream().filter(function(m){m['a']>10});
    

    不幸的是,JEXL 中的方法解析器无法使用 JEXL 函数正确解析 Stream.filter(Predicate),因为它不知道如何将 JEXL 函数转换为 Predicate。 JEXL 函数的类型为 org.apache.commons.jexl3.internal.Closure

    因此,您至少需要在 Java 中提供您自己的 Predicate 实现,然后在您的脚本中创建一个新实例:

    public class MyCustomFilterPredicate implements Predicate<HashMap<String, Integer>> {
        @Override
        public boolean test(final HashMap<String, Integer> m)
        {
            return m.get("a") > 10;
        }
    }
    

    然后您可以在 JEXL 脚本中创建一个新实例:

    var filterPredicate = new('my.custom.filter.predicate.MyCustomFilterPredicate');
    

    Stream.map(Function) 也是如此:

    public class MyCustomMapFunction implements Function<HashMap<String, Integer>, Integer> {
        @Override
        public Integer apply(final HashMap<String, Integer> m)
        {
            return m.get("b");
        }
    }
    

    再次在您的脚本中创建一个新实例:

    var mapFunction = new('my.custom.map.function.MyCustomMapFunction');
    

    您的整个脚本将如下所示:

    var x = {{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}};
    
    var filterPredicate = new('my.custom.filter.predicate.MyCustomFilterPredicate');
    var mapFunction = new('my.custom.map.function.MyCustomMapFunction');
    
    return x.stream().filter(filterPredicate).map(mapFunction).toArray();
    

    当然,您可能已经注意到谓词和函数实现的可重用性相当有限。这就是为什么我建议创建包装 JEXL 闭包的实现:

    public class MyCustomFilterPredicate implements Predicate<Object> {
        private final Closure closure;
        public MyCustomFilterPredicate(final Closure closure) {
            this.closure = closure;
        }
        @Override
        public boolean test(final Object o)
        {
            return (boolean) closure.execute(JexlEngine.getThreadContext(), o);
        }
    }
    
    public class MyCustomMapFunction implements Function<Object, Object> {
        private final Closure closure;
        public MyCustomMapFunction(final Closure closure) {
            this.closure = closure;
        }
        @Override
        public Object apply(final Object o)
        {
            return closure.execute(JexlEngine.getThreadContext(), o);
        }
    }
    

    现在您可以按如下方式更改脚本并以各种方式重用这些 Java 类:

    var x = {{'a':11,'b':5}, {'a':1,'b':15}, {'a':12,'b':25}, {'a':4,'b':35}, {'a':7,'b':45}};
    
    var filterPredicate = new('my.custom.filter.predicate.MyCustomFilterPredicate', function(m){m['a']>10});
    var mapFunction = new('my.custom.map.function.MyCustomMapFunction', function(m){m['b']});
    
    return x.stream().filter(filterPredicate).map(mapFunction).toArray();
    

    【讨论】:

    • 非常感谢您的详细解答。顺便说一句,我正在为技术较少的用户开发脚本语言,有没有更简单的方法来访问这些过滤器,而不是提供 function(){} 方式?
    • 使用纯 JEXL,没有。但当然总有办法,这取决于你愿意和能够走多远。例如,您可以扩展 JEXL 库并编写自己的解析器来添加一些糖。或者您可能想研究类似 xtext 的东西,它可以让您轻松开发自己的语言。然而,这肯定会超出这个问题的范围。祝你好运!
    • 我们可以扩展 JEXL 解析器吗?实际上,我尝试过但找不到这样做的方法,尽管我可以覆盖运算符。
    • 嗯,它是开源的,所以你当然可以。但我建议为此提出一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2020-12-02
    • 2016-08-27
    • 2016-08-06
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 2019-08-25
    • 2021-02-27
    相关资源
    最近更新 更多