From 6300fb37adf7ce81fa2fe2b23e6e8cf40ff9c3c1 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 27 Sep 2023 16:35:31 +0200 Subject: [PATCH] Include '?' for null-safe navigation in SpEL AST representations Prior to this commit, if a Spring Expression Language (SpEL) expression contained property, field, or method references using the null-safe navigation operator (?.), the generated AST String representation incorrectly omitted the '?' characters. For example, 'myProperty?.myMethod()' had a generated AST string representation of 'myProperty.myMethod()'. This commit addresses this by introducing isNullSafe() in MethodReference and reworking the logic in CompoundExpression.toStringAST(). Closes gh-31326 --- .../expression/spel/ast/CompoundExpression.java | 8 +++++++- .../expression/spel/ast/MethodReference.java | 9 +++++++++ .../springframework/expression/spel/ParsingTests.java | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) 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