diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java index 01745e65cfc6..8a540a908dc6 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java @@ -28,7 +28,9 @@ /** * Represents a DOT separated expression sequence, such as - * {@code property1.property2.methodOne()}. + * {@code property1.property2.methodOne()} or + * {@code property1?.property2?.methodOne()} when the null-safe navigation + * operator is used. * *

May also contain array/collection/map indexers, such as * {@code property1[0].property2['key']}. @@ -122,6 +124,10 @@ public String toStringAST() { // Don't append a '.' if the next child is an Indexer. // For example, we want 'myVar[0]' instead of 'myVar.[0]'. if (!(nextChild instanceof Indexer)) { + if ((nextChild instanceof MethodReference methodRef && methodRef.isNullSafe()) || + (nextChild instanceof PropertyOrFieldReference pofRef && pofRef.isNullSafe())) { + sb.append('?'); + } sb.append('.'); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 33756cab0e78..e79e6a9affeb 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -50,6 +50,7 @@ * * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ public class MethodReference extends SpelNodeImpl { @@ -72,6 +73,14 @@ public MethodReference(boolean nullSafe, String methodName, int startPos, int en } + /** + * Does this node represent a null-safe method reference? + * @since 6.0.13 + */ + public final boolean isNullSafe() { + return this.nullSafe; + } + /** * Get the name of the referenced method. */ diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java index eea5e2e33330..6eeab7d6180b 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java @@ -47,6 +47,7 @@ class Miscellaneous { void compoundExpressions() { parseCheck("property1.property2.methodOne()"); parseCheck("property1[0].property2['key'].methodOne()"); + parseCheck("property1?.methodOne()?.property2?.methodTwo()"); } @Test