Extending MVEL with Custom Functions: The Right Way to Use 'in' for Full Containment
How to Handle Multi-Value “In” Logic in MVEL When Native Support Falls Short
Wiki topics:
💻 · Programming
Extending MVEL with Custom Functions: The Right Way to Use 'in' for Full Containment
How to Handle Multi-Value “In” Logic in MVEL When Native Support Falls Short
Problem Overview
MVEL (MVFLEX Expression Language) is widely used for dynamic expression evaluation in Java-based rule engines. However, it does not support the in keyword out-of-the-box the way you might expect:
rating in ['Excellent', 'Good', 'Normal'] // ❌ Will throw error
While MVEL does support .contains(...), it's strictly for string matching or manual collections — and has no concept of multi-value containment like SQL’s IN.
Goal
We want to support expressions like:
inList(overallRating, ['Excellent', 'Good', 'Normal'])
Where:
ratingcan be a single value like"Good"or a list of values like["Good", "Normal"]- The match is considered
trueOnly if all values are contained in the provided list
Step 1 — Define the Custom Function
public class CustomFunctions {
/**
* Check if the value is fully contained in the list.
* Supports both single values and collections.
*/
public static boolean inList(Object value, Collection<?> list) {
if (value == null || list == null) return false;
if (value instanceof Collection<?>) {
return list.containsAll((Collection<?>) value); // ✅ must match all
}
return list.contains(value);
}
}
Step 2 — Register the Function with MVEL
import org.mvel2.MVEL;
import org.mvel2.ParserContext;
import java.io.Serializable;
import java.util.*;
public class MvelExpressionRunner {
public static boolean runExpression(String expression, Map<String, Object> context) {
ParserContext parserContext = new ParserContext();
try {
parserContext.addImport("inList", CustomFunctions.class.getMethod(
"inList", Object.class, Collection.class));
} catch (NoSuchMethodException e) {
throw new RuntimeException("Failed to register custom method", e);
}
Serializable compiled = MVEL.compileExpression(expression, parserContext);
return (Boolean) MVEL.executeExpression(compiled, context);
}
}
Step 3 — Full JUnit Tests
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
class MvelCustomFunctionTest {
@Test
void testSingleValueMatch() {
Map<String, Object> ctx = Map.of(
"rating", "B",
"validRatings", List.of("A", "B", "C")
);
String expr = "inList(rating, validRatings)";
assertTrue(MvelExpressionRunner.runExpression(expr, ctx));
}
@Test
void testMultiValueExactMatch() {
Map<String, Object> ctx = Map.of(
"rating", List.of("B", "C"),
"validRatings", List.of("A", "B", "C")
);
String expr = "inList(rating, validRatings)";
assertTrue(MvelExpressionRunner.runExpression(expr, ctx));
}
@Test
void testMultiValuePartialFail() {
Map<String, Object> ctx = Map.of(
"rating", List.of("B", "X"),
"validRatings", List.of("A", "B", "C")
);
String expr = "inList(rating, validRatings)";
assertFalse(MvelExpressionRunner.runExpression(expr, ctx)); // ✅ fails now
}
@Test
void testFullLogicalRule() {
Map<String, Object> ctx = new HashMap<>();
ctx.put("age", 90);
ctx.put("status", "On");
ctx.put("rating", List.of("Excellent", "Good"));
ctx.put("validRatings", List.of("Excellent", "Good", "Normal"));
String expr = "age < 94 && status == 'On' && inList(rating, validRatings)";
assertTrue(MvelExpressionRunner.runExpression(expr, ctx));
}
}
Summary

🚀 Final Thoughts
With this setup, you can:
- Write flexible rules in MVEL using expressive DSL
- Handle advanced logic (multi-value match, containment) without post-processing
- Cleanly separate logic and code via a rule engine
That’s it.
Thanks for reading! If you like it or feel it helped, pls click Applaud. Happy coding.
See you next time :)
메타데이터
- post_id
- 35d51d01b4c8
- slug
- extending-mvel-with-custom-functions-the-right-way-to-use-in-for-full-containment-35d51d01b4c8
- url
- https://levelup.gitconnected.com/extending-mvel-with-custom-functions-the-right-way-to-use-in-for-full-containment-35d51d01b4c8
- canonical_url
- https://levelup.gitconnected.com/extending-mvel-with-custom-functions-the-right-way-to-use-in-for-full-containment-35d51d01b4c8
- author_url
- https://medium.com/@bayern01kahn
- status
- ok
- fetched_at
- 2026-07-19 07:01:35