java实现简单的字符串解析匹配运算规则引擎

2023-03-09 21:13:13 浏览数 (2)

有这样的需求,我有一个map,里面放了一些key-value,自定义了一些规则,如age==24&&name==aom||phone==123456789,希望能有个引擎能判断出这个Map里的值,是否匹配上这个规则,规则里有一些简单的运算,如==、contains等。

规则是动态可变的,这样就可以灵活控制命中了规则的数据能进行一些采集。

我做了一个这样简单的工具,目前可以支持

代码语言:javascript复制
//规则描述,支持的有:
    //==,如 age==25,name==jerry,字符串也不要加引号
    //!=,和==一样的用法
    //&&、||,如age==24&&name==aom||phone==123456789,不要加括号,自行调整好顺序
    //contains,如address contains(example),字符串不要加引号
    //matches,如phone matches(d ),正则表达式
    //in,是否在一个集合里,如age in [12,1,25],集合需要用[]包围,字符串不要引号
    //isEmpty,是否为空,如address isEmpty
    //isNotEmpty,同isEmpty
    //!(),取非操作,如 !(address isNotEmpty),注意,!必须在最前面,要取非的,需要用小括号包围

这些常用的一些基本规则。

类似于的表达式还有spring的SpEL、mvel这些表达式引擎,但我的场景对性能的要求相当苛刻,规则倒是很简单,是无法接受这些动态规则引擎的高达十几甚至20ms的耗时。对这一个规则匹配的耗时要求不能超过1ms,所以就自己做了一个。

下面是代码

代码语言:javascript复制
import java.util.Map;

/**
 * 规则校验器
 * @author wuweifeng wrote on 2023/3/7
 * @version 1.0
 */
public class RuleValidator {
    public static boolean validateRule(String rule, Map<String, String> variables) {
        if (rule == null || "".equals(rule)) {
            return false;
        }
        // 去除所有空格
        rule = rule.replaceAll("\s ", "");

        //判断表达式是不是取非语句,如" !(a==345) "
        if (rule.startsWith("!(") && rule.endsWith(")")) {
            return !validateRule(rule.substring(2, rule.length() - 1), variables);
        }

        // 处理 || 运算符
        if (rule.contains("||")) {
            int index = rule.indexOf("||");
            String left = rule.substring(0, index);
            String right = rule.substring(index   2);
            return validateRule(left, variables) || validateRule(right, variables);
        }

        // 处理 && 运算符
        if (rule.contains("&&")) {
            int index = rule.indexOf("&&");
            String left = rule.substring(0, index);
            String right = rule.substring(index   2);
            return validateRule(left, variables) && validateRule(right, variables);
        }

        // 处理括号运算符
        if (rule.startsWith("(") && rule.endsWith(")")) {
            String subrule = rule.substring(1, rule.length() - 1);
            return validateRule(subrule, variables);
        }

        // 处理 == 运算符和 != 运算符
        if (rule.contains("==")) {
            int index = rule.indexOf("==");
            String left = rule.substring(0, index);
            String right = rule.substring(index   2);
            if (variables.containsKey(left)) {
                return variables.get(left).equals(right);
            } else {
                return left.equals(right);
            }
        } else if (rule.contains("!=")) {
            int index = rule.indexOf("!=");
            String left = rule.substring(0, index);
            String right = rule.substring(index   2);
            if (variables.containsKey(left)) {
                return !variables.get(left).equals(right);
            } else {
                return !left.equals(right);
            }
        }

        // 处理 contains 运算符和 in 运算符
        if (rule.contains("contains")) {
            int index = rule.indexOf("contains");
            String left = rule.substring(0, index).replace(".", "");
            String value = variables.get(left);
            String right = rule.substring(index   8).replace("(", "").replace(")", "");
            if (variables.containsKey(left)) {
                return value.contains(right);
            } else {
                return false;
            }
        } else if (rule.contains("in")) {
            int index = rule.indexOf("in");
            String left = rule.substring(0, index);
            String value = variables.get(left);
            String right = rule.substring(index   2).replace("[", "").replace("]", "");
            if (variables.containsKey(right)) {
                String[] items = variables.get(right).split(",");
                for (String item : items) {
                    if (item.trim().equals(value)) {
                        return true;
                    }
                }
                return false;
            } else {
                String[] items = right.split(",");
                for (String item : items) {
                    if (item.trim().equals(value)) {
                        return true;
                    }
                }
                return false;
            }
        }

        // 处理 isEmpty 运算符
        if (rule.contains("isEmpty")) {
            int index = rule.indexOf("isEmpty");
            String left = rule.substring(0, index);
            if (variables.containsKey(left)) {
                return variables.get(left) == null || "".equals(variables.get(left));
            } else {
                return false;
            }
        }

        // 处理 isEmpty 运算符
        if (rule.contains("isNotEmpty")) {
            int index = rule.indexOf("isNotEmpty");
            String left = rule.substring(0, index);
            if (variables.containsKey(left)) {
                return variables.get(left) != null && !"".equals(variables.get(left));
            } else {
                return false;
            }
        }

        // 处理 matches 运算符
        if (rule.contains("matches")) {
            int index = rule.indexOf("matches");
            String left = rule.substring(0, index);
            String right = rule.substring(index   7);
            if (variables.containsKey(left)) {
                return variables.get(left).matches(right);
            } else {
                return left.matches(right);
            }
        }

        // 如果规则字符串为变量名,则直接查找Map中对应的值
        if (variables.containsKey(rule)) {
            return true;
        }

        return false;

    }
}
代码语言:javascript复制
import java.util.HashMap;
import java.util.Map;

/**
 * @author wuweifeng wrote on 2023/3/7
 * @version 1.0
 */
public class RuleValidatorTest {
    //规则描述,支持的有:
    //==,如 age==25,name==jerry,字符串也不要加引号
    //!=,和==一样的用法
    //&&、||,如age==24&&name==aom||phone==123456789,不要加括号,自行调整好顺序
    //contains,如address contains(example),字符串不要加引号
    //matches,如phone matches(d ),正则表达式
    //in,是否在一个集合里,如age in [12,1,25],集合需要用[]包围,字符串不要引号
    //isEmpty,是否为空,如address isEmpty
    //isNotEmpty,同isEmpty
    //!(),取非操作,如 !(address isNotEmpty),注意,!必须在最前面,要取非的,需要用小括号包围


    public static void main(String[] args) {
        // 初始化变量Map
        Map<String, String> variables = new HashMap<>();
        variables.put("name", "Tom");
        variables.put("age", "25");
        variables.put("email", "tom@example.com");
        variables.put("phone", "123456789");
        variables.put("address", "");

        // 测试用例
        String rule1 = "age==25";
        String rule2 = "age!=25";
        String rule3 = "age==30";
        String rule4 = "age!=30";

        String rule5 = "age==24&&name==aom||phone==123456789";
        String rule6 = "age==25||name==Jerry";

        String rule7 = "address contains(example)";

        //正则表达式
        String rule8 = "phone matches(\d )";

        String rule9 = "age in [12,1,25]";

        String rule10 = "age in [12,1,25] && name==Tom";

        String rule11 = "address isEmpty";
        String rule12 = "address isNotEmpty";

        String rule13 = "!(address isNotEmpty)";

        // 断言
        System.out.println("rule1-"   RuleValidator.validateRule(rule1, variables));
        System.out.println("rule2-"    RuleValidator.validateRule(rule2, variables));
        System.out.println("rule3-"    RuleValidator.validateRule(rule3, variables));
        System.out.println("rule4-"    RuleValidator.validateRule(rule4, variables));
        System.out.println("------");

        System.out.println("rule5-"    RuleValidator.validateRule(rule5, variables));
        System.out.println("rule6-"    RuleValidator.validateRule(rule6, variables));
        System.out.println("rule7-"    RuleValidator.validateRule(rule7, variables));
        System.out.println("------");

        System.out.println("rule8-"    RuleValidator.validateRule(rule8, variables));


        System.out.println("------");
        System.out.println("rule9-"   RuleValidator.validateRule(rule9, variables));
        System.out.println("rule10-"   RuleValidator.validateRule(rule10, variables));

        System.out.println("rule11-"   RuleValidator.validateRule(rule11, variables));
        System.out.println("rule12-"   RuleValidator.validateRule(rule12, variables));
        System.out.println("rule13-"   RuleValidator.validateRule(rule13, variables));
    }
}

整体比较简单,不支持复杂的(),最好是平铺的一些规则。

0 人点赞