Reputation: 99
I have a method evaluateExpression
present in a different library and that library is using Jexl3 for evaluating expressionString
passed to evaluateExpression
method.
inputMap
contains the object that is used for evaluating the expressionString on. This is the second parameter to that function.
expressionString
is record.getDriversMap().values().stream().filter(myDriver -> 'Fav_Driver'.equals(vtd.getAttributeName())).collect(Collectors.toList())
record
is a class which contains driversMap
whose values are of type MyDriver
class and it has an attributeName
field. I have added a filter condition and tried to collect the list. Doing this I was getting an exception org.apache.commons.jexl3.JexlException$Parsing
with message (
which is unclear. Does anyone know how to resolve this issue.
It would be great if someone provides examples of jexl expression with stream, map and collect ? Below is the method present in the library.
Object evaluateExpression(String expressionString, Map<String, Object> inputMap) {
MapContext mapContext = new MapContext(inputMap);
JexlExpression jexlExpression = this.jexlEngine.createExpression(expressionString);
return jexlExpression.evaluate(mapContext);
}
Upvotes: 0
Views: 1313
Reputation: 364
JEXL offers many ways to achieve a tight integration but it requires some code to do so.
Not sure if JEXLs integration in your library allows this but JEXL misses some information to achieve what you seek. For JEXL to filter streams, it needs the definition of how to to do that.
The example below (running on JEXL 3.3 SNAPSHOT) does illustrate one way of doing so by extending the JexlContext (could also be done by extending the JexlArithmetic).
/**
* Mock driver.
*/
public static class Driver0930 {
private String name;
Driver0930(String n) {
name = n;
}
public String getAttributeName() {
return name;
}
}
public static class Context0930 extends MapContext {
/**
* This allows using a JEXL lambda as a filter.
* @param stream the stream
* @param filter the lambda to use as filter
* @return the filtered stream
*/
public Stream<?> filter(Stream<?> stream, JexlScript filter) {
return stream.filter(x -> Boolean.TRUE.equals(filter.execute(this, x)));
}
}
@Test
public void testStackOvflw20220930() {
// fill some drivers in a list
List<Driver0930> values = new ArrayList<>();
for(int i = 0; i < 8; ++i) {
values.add(new Driver0930("drvr" + Integer.toOctalString(i)));
}
for(int i = 0; i < 4; ++i) {
values.add(new Driver0930("favorite" + Integer.toOctalString(i)));
}
// Use a context that can filter and that exposes Collectors
JexlEngine jexl = new JexlBuilder().safe(false).create();
JexlContext context = new Context0930();
context.set("values", values);
context.set("Collectors", Collectors.class);
// The script with a JEXL 3.2 (lambda function) and 3.3 syntax (lambda expression)
String src32 = "values.stream().filter((driver) ->{ driver.attributeName =^ 'favorite' }).collect(Collectors.toList())";
String src33 = "values.stream().filter(driver -> driver.attributeName =^ 'favorite').collect(Collectors.toList())";
for(String src : Arrays.asList(src32, src33)) {
JexlExpression s = jexl.createExpression(src);
Assert.assertNotNull(s);
Object r = s.evaluate(context);
Assert.assertNotNull(r);
// got a filtered list of 4 drivers whose attribute name starts with 'favorite'
List<Driver0930> l = (List<Driver0930>) r;
Assert.assertEquals(4, l.size());
for (Driver0930 d : l) {
Assert.assertTrue(d.getAttributeName().startsWith("favorite"));
}
}
}
Upvotes: 1