java 的第一个分享,一个富有表现力的表达式引擎的 Java 实现

一个富有语言表现力的表达式引擎 Java 实现。

先附上项目地址:github.com/slince/expression

表达式引擎在 java 里有很多个知名实现,比如 spring 的 spel 和 apache 的 jexl,都是非常优秀的作品;那么为什么要选择实现表达式引擎呢;主要因为这么几个原因;

对编译原理的兴趣

作者是 phper 出身, php 里有两个非常知名的作品,模板引擎 twig 和 symfony 的 expression language,都是出自大神 fabpot,因为好奇的原因研究过实现自此就感受到了编译的乐趣,后面自己看了一些抽象语法树,词法分析器,语法解析器的文章和案例。随着深入,便渐渐萌生了也写一个的念头;于是 typescript 版的 expression-language.js 和 java 版的本项目就诞生了;

现有表达式引擎的不足

java 代码中对对象的判空是非常常见的,但在表达式里判断并进行逻辑转换就比较麻烦,比如如下表达式

Evaluator.evaluate("user.tags.size()")

上述表达式意图得到用户的标签的数量,在对象均不为 null 的情况下是没有问题的,一旦 tagsnull,运行便会报空指针异常;

如果是 java 代码我们可以判空:如果为空则强行返回0;

为了在表达式里实现此效果,本实现里借用了 twig 的 filter 语法

Evaluator.evaluate("user.tags|size")

过滤器 size 会判断是否是 null; 表达式内置了一些过滤器 github.com/slince/expression/blob/...

当然你也可以扩展表达式引擎提供自己的过滤器;

JSONPATH的灵感

如果大家用过 json path 的话会惊讶于 json path 取子代元素的的灵巧,jsonpath 只能处理 json , 在对象上是不适用的,因此在本实现里,借鉴了部分 json path 的功能

  • 搜索子代同名属性

    Evaluator.evaluate("user..name")

    上述表达式会将 user 对象的所有属性以及属性对象的属性中名称为 name的属性值遍历出来

  • 平铺遍历 list对象集合的指定属性

Evaluator.evaluate("user.books|fluent.author.name.collect()")

上述表达式可以把某用户的所有书籍的作者提取出来;fluent 过滤器会把 List<Book> 转换成类 Fluency<Book>,以方面平滑的获取每一个 Book 的作者的名称;

执行过程类似 java 代码:

user.books.stream().filter(v -> v.getAuthor().getName()).collect(Collector.toList());

安装

<dependency>
    <groupId>io.github.slince</groupId>
    <artifactId>expression</artifactId>
    <version>0.0.2-RELEASE</version>
</dependency>

ObjectPath:

<dependency>
    <groupId>io.github.slince</groupId>
    <artifactId>expression-data-path</artifactId>
    <version>0.0.2-RELEASE</version>
</dependency>

ObjectPath 使用文档见这里

快速开始

import io.github.slince.expression.Evaluator;
import io.github.slince.expression.MapContext;

import java.util.Arrays;
import java.util.List;

public class Book {

    public static void main(String[] args) {
        // 创建一本书实例
        Book book = new Book("The Lady of the Camellias", 12.89f, Arrays.asList("Love Story", "France", null));
        MapContext ctx = new MapContext();
        ctx.setVar("book", book);

        // 执行表达式
        // "The Lady of the Camellias"
        System.out.println(Evaluator.INSTANCE.evaluate("book.name", ctx));
        // 3
        System.out.println(Evaluator.INSTANCE.evaluate("book.tags|size", ctx));
        // 22.89f
        System.out.println(Evaluator.INSTANCE.evaluate("book.price + 10", ctx));
    }

    private final String name;
    private final float price;
    private final List<String> tags;

    public Book(String name, float price, List<String> tags) {
        this.name = name;
        this.price = price;
        this.tags = tags;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }

    public List<String> getTags() {
        return tags;
    }
} 

文档

更多文档请查看详细文档

问题反馈

报告 Issue: github issues

LICENSE

The Apache 2.0 license. See Apache-2.0

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!