15

Is it possible for stream or forEach on list in SpEL? e.g.

List<String> x = new LinkedList<>(Arrays.asList("A","AAB"));
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(x);
parser.parseExpression("x.stream().map(x -> x.replaceAll(\"A\", \"B\")).collect(Collectors.toList())").getValue(context))
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
Mateusz Sobczak
  • 1,551
  • 8
  • 33
  • 67

3 Answers3

16

SpEL is not Java, it's a different language; the acronym stands for Spring Expression Language.

It doesn't understand Java8 lambdas so can't parse x -> ....

Also, static methods are invoked with the T operator.

So, this works...

List<String> x = new LinkedList<>(Arrays.asList("A","AAB"));
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("stream().collect(T(java.util.stream.Collectors).toList())");
System.out.println(expression.getValue(x));

(but it's not very useful).

You can use streams, but only with simple methods that don't take lambdas...

Expression expression = parser.parseExpression("stream().findFirst().get()");
Expression expression = parser.parseExpression("stream().count()");

or

List<String> x = new LinkedList<>(Arrays.asList("A","AAB", "A"));
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("stream().distinct().collect(T(java.util.stream.Collectors).toList())");
System.out.println(expression.getValue(x));

etc

EDIT

You can, however, register lambdas as SpEL #functions, so this works fine...

public class So48840190Application {

    public static void main(String[] args) throws Exception {
        List<String> x = new LinkedList<>(Arrays.asList("A","AAB", "A"));
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext ec = new StandardEvaluationContext();
        ec.registerFunction("aToB", So48840190Application.class.getMethod("aToB"));
        Expression expression = parser.parseExpression(
                "stream().map(#aToB()).collect(T(java.util.stream.Collectors).toList())");
        System.out.println(expression.getValue(ec, x));
    }

    public static Function<String, String> aToB() {
        return s -> s.replaceAll("A", "B");
    }

}

and

[B, BBB, B]

EDIT2

Or, more generally...

public class So48840190Application {

    public static void main(String[] args) throws Exception {
        List<String> x = new LinkedList<>(Arrays.asList("A","AAB", "A"));
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext ec = new StandardEvaluationContext();
        ec.registerFunction("replaceAll",
                So48840190Application.class.getMethod("replaceAll", String.class, String.class));
        ec.registerFunction("toLowerCase",
                So48840190Application.class.getMethod("toLowerCase"));
        Expression expression = parser.parseExpression(
                "stream().map(#replaceAll('A', 'B')).map(#toLowerCase()).collect(T(java.util.stream.Collectors).toList())");
        System.out.println(expression.getValue(ec, x));
    }

    public static Function<String, String> replaceAll(String from, String to) {
        return s -> s.replaceAll(from, to);
    }

    public static Function<String, String> toLowerCase() {
        return String::toLowerCase;
    }

}
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • this is probably just an old habit I guess, but `? extends String` makes no sense, since String is final. While you can do `? super String` it would make little sense probably too – Eugene Feb 17 '18 at 21:48
  • 2
    Don't blame me :) That was created by Eclipse refactoring (convert lambda to method using `Refactor | Extract Method`). I guess it's a bug in eclipse then. – Gary Russell Feb 17 '18 at 22:29
  • Added a more general `replaceAll` lambda `#function`. – Gary Russell Feb 18 '18 at 21:49
1

You can use the #{T( ... )} to invoke a static method which does the mapping or whatever you need. Example When writing a query and using spel:

@Query(value = """
    ...
    WHERE foo IN :#{T(com.example.MyRepository).convertSet(#my_set)}
    ...
"""
, nativeQuery = true)
Bla findByFooIn(@Param("my_set") Set<String> mySet);

static Set<Foo> convertSet(Set<String> mySet) {
    return mySet.stream().map(s -> new Foo(s)).collect(Collectors.toSet());
}
Mohammad
  • 11
  • 1
0

U can use loop in MVEL. see https://github.com/XhinLiang/arthas-mvel

$ count = 1
@Integer[1]
$ for (int i =0; i < 100; i++) { count = count + 1;}
null
$ count
@Integer[101]
xhinliang
  • 164
  • 1
  • 7