简体   繁体   中英

Library for evaluating logical expressions on Java objects

Let's say I have the following class:

class Person {
    int age;
    String city;
    Collection<Person> friends;
    Person spouse;
}

I need a library which would allow me to evaluate whether a logical expression is true on a given Person object. The expression would look something like this:

((age>25 OR spouse.age>27) AND city=="New-York" AND size(friends)>100)

So, the requirements are:

  1. Ability to use basic logical operators
  2. Access properties of the given object
  3. Access properties of an internal object
  4. Use simple mathematical\sql-like functions such as size,max,sum

Suggestions?

You could use a ScriptEngine + reflection:

  • access all the fields in your object and create variable that have those values
  • evaluate the expression

Here is a contrived example which outputs:

age = 35
city = "London"
age > 32 && city == "London" => true
age > 32 && city == "Paris" => false
age < 32 && city == "London" => false

It could become quite messy if you want to deal with non primitive types, such as collections.

public class Test1 {

    public static void main(String[] args) throws Exception{
        Person p = new Person();
        p.age = 35;
        p.city = "London";

        ScriptEngineManager factory = new ScriptEngineManager();
        ScriptEngine engine = factory.getEngineByName("JavaScript");

        Class<Person> c = Person.class;
        for (Field f : c.getDeclaredFields()) {
            Object o = f.get(p);
            String assignement = null;
            if (o instanceof String) {
                assignement = f.getName() + " = \"" + String.valueOf(o) + "\"";
            } else {
                assignement = f.getName() + " = " + String.valueOf(o);
            }
            engine.eval(assignement);
            System.out.println(assignement);
        }

        String condition = "age > 32 && city == \"London\"";
        System.out.println(condition + " => " + engine.eval(condition));

        condition = "age > 32 && city == \"Paris\"";
        System.out.println(condition + " => " + engine.eval(condition));

        condition = "age < 32 && city == \"London\"";
        System.out.println(condition + " => " + engine.eval(condition));
    }

    public static class Person {

        int age;
        String city;
    }
}

OK, think I found what I wanted, it's a variation on assylias' answer but I prefer it since it's more standardized (woudn't want to depend specifically on Javascript expressions, or run Javascript at all for that matter).

Apparently there is a Unified Expression Language for evaluating expressions, it was originally designed for JSP but can be used for other stuff as well. There are several parser implementations, I'll probably go either with Spring EL or JUEL

You can use .https://github.com/mozilla/rhino

Example :

@Test
public void shouldCheckLogicalOperations(){
    Context cx = Context.enter();
    try {
        Scriptable scope = cx.initStandardObjects();
        ScriptableObject.putProperty(scope, "form_admin_checked", true);
        ScriptableObject.putProperty(scope, "form_limit_value", 50000);

        String script = "((form_admin_checked && form_limit_value<2000) || (form_admin_checked && !form_admin_checked) || form_admin_checked && form_limit_value==50000)";
        String expectedResponse = "true";

        Object result = cx.evaluateString(scope, script, "<cmd>", 1, null);

        Assert.assertEquals(expectedResponse, result.toString());

    } finally {
        // Exit from the context.
        Context.exit();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM