Skip to content

Commit d81f1a5

Browse files
committed
Test SpEL Map access/indexing support for nonexistent keys
This commit introduces two tests to verify the status quo. - mapAccessThroughIndexerForNonexistentKey(): demonstrates that map access via the built-in support in the Indexer returns `null` for a nonexistent key. - nullAwareMapAccessor(): demonstrates that users can implement and register a custom extension of MapAccessor which reports that it can read any map (ignoring whether the map actually contains an entry for the given key) and returns `null` for a nonexistent key. See gh-35534
1 parent eb11070 commit d81f1a5

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
import org.springframework.expression.EvaluationContext;
2424
import org.springframework.expression.TypedValue;
2525
import org.springframework.expression.spel.standard.SpelExpressionParser;
26+
import org.springframework.expression.spel.support.MapAccessor;
2627

2728
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
30+
import static org.springframework.expression.spel.SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE;
2831

2932
/**
3033
* Testing variations on map access.
@@ -43,6 +46,11 @@ void mapAccessThroughIndexer() {
4346
evaluate("testMap['monday']", "montag", String.class);
4447
}
4548

49+
@Test
50+
void mapAccessThroughIndexerForNonexistentKey() {
51+
evaluate("testMap['bogus']", null, String.class);
52+
}
53+
4654
@Test
4755
void variableMapAccess() {
4856
var parser = new SpelExpressionParser();
@@ -80,10 +88,48 @@ void mapAccessor() {
8088

8189
var expr1 = parser.parseExpression("testMap.monday");
8290
assertThat(expr1.getValue(ctx, String.class)).isEqualTo("montag");
91+
92+
var expr2 = parser.parseExpression("testMap.bogus");
93+
assertThatExceptionOfType(SpelEvaluationException.class)
94+
.isThrownBy(() -> expr2.getValue(ctx, String.class))
95+
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(PROPERTY_OR_FIELD_NOT_READABLE));
96+
}
97+
98+
@Test
99+
void nullAwareMapAccessor() {
100+
var parser = new SpelExpressionParser();
101+
var ctx = TestScenarioCreator.getTestEvaluationContext();
102+
ctx.addPropertyAccessor(new NullAwareMapAccessor());
103+
104+
var expr = parser.parseExpression("testMap.monday");
105+
assertThat(expr.getValue(ctx, String.class)).isEqualTo("montag");
106+
107+
// Unlike MapAccessor, NullAwareMapAccessor returns null for a nonexistent key.
108+
expr = parser.parseExpression("testMap.bogus");
109+
assertThat(expr.getValue(ctx, String.class)).isNull();
83110
}
84111

85112

86113
record TestBean(Map<String, String> properties, TestBean nestedBean) {
87114
}
88115

116+
117+
/**
118+
* In contrast to the standard {@link MapAccessor}, {@code NullAwareMapAccessor}
119+
* reports that it can read any map (ignoring whether the map actually contains
120+
* an entry for the given key) and returns {@code null} for a nonexistent key.
121+
*/
122+
private static class NullAwareMapAccessor extends MapAccessor {
123+
124+
@Override
125+
public boolean canRead(EvaluationContext context, Object target, String name) {
126+
return (target instanceof Map);
127+
}
128+
129+
@Override
130+
public TypedValue read(EvaluationContext context, Object target, String name) {
131+
return new TypedValue(((Map<?, ?>) target).get(name));
132+
}
133+
}
134+
89135
}

0 commit comments

Comments
 (0)