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
This commit is contained in:
parent
eb11070c19
commit
d81f1a55c2
|
@ -23,8 +23,11 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.TypedValue;
|
import org.springframework.expression.TypedValue;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.MapAccessor;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.springframework.expression.spel.SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Testing variations on map access.
|
* Testing variations on map access.
|
||||||
|
@ -43,6 +46,11 @@ class MapAccessTests extends AbstractExpressionTests {
|
||||||
evaluate("testMap['monday']", "montag", String.class);
|
evaluate("testMap['monday']", "montag", String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mapAccessThroughIndexerForNonexistentKey() {
|
||||||
|
evaluate("testMap['bogus']", null, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void variableMapAccess() {
|
void variableMapAccess() {
|
||||||
var parser = new SpelExpressionParser();
|
var parser = new SpelExpressionParser();
|
||||||
|
@ -80,10 +88,48 @@ class MapAccessTests extends AbstractExpressionTests {
|
||||||
|
|
||||||
var expr1 = parser.parseExpression("testMap.monday");
|
var expr1 = parser.parseExpression("testMap.monday");
|
||||||
assertThat(expr1.getValue(ctx, String.class)).isEqualTo("montag");
|
assertThat(expr1.getValue(ctx, String.class)).isEqualTo("montag");
|
||||||
|
|
||||||
|
var expr2 = parser.parseExpression("testMap.bogus");
|
||||||
|
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||||
|
.isThrownBy(() -> expr2.getValue(ctx, String.class))
|
||||||
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(PROPERTY_OR_FIELD_NOT_READABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nullAwareMapAccessor() {
|
||||||
|
var parser = new SpelExpressionParser();
|
||||||
|
var ctx = TestScenarioCreator.getTestEvaluationContext();
|
||||||
|
ctx.addPropertyAccessor(new NullAwareMapAccessor());
|
||||||
|
|
||||||
|
var expr = parser.parseExpression("testMap.monday");
|
||||||
|
assertThat(expr.getValue(ctx, String.class)).isEqualTo("montag");
|
||||||
|
|
||||||
|
// Unlike MapAccessor, NullAwareMapAccessor returns null for a nonexistent key.
|
||||||
|
expr = parser.parseExpression("testMap.bogus");
|
||||||
|
assertThat(expr.getValue(ctx, String.class)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
record TestBean(Map<String, String> properties, TestBean nestedBean) {
|
record TestBean(Map<String, String> properties, TestBean nestedBean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In contrast to the standard {@link MapAccessor}, {@code NullAwareMapAccessor}
|
||||||
|
* reports that it can read any map (ignoring whether the map actually contains
|
||||||
|
* an entry for the given key) and returns {@code null} for a nonexistent key.
|
||||||
|
*/
|
||||||
|
private static class NullAwareMapAccessor extends MapAccessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRead(EvaluationContext context, Object target, String name) {
|
||||||
|
return (target instanceof Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypedValue read(EvaluationContext context, Object target, String name) {
|
||||||
|
return new TypedValue(((Map<?, ?>) target).get(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue