Skip to content

Commit 218a148

Browse files
committed
Document null-safe index operator in SpEL
See gh-29847
1 parent 38c473f commit 218a148

File tree

4 files changed

+88
-1
lines changed

4 files changed

+88
-1
lines changed

framework-docs/modules/ROOT/pages/core/expressions/language-ref/operator-safe-navigation.adoc

+59-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[[expressions-operator-safe-navigation]]
22
= Safe Navigation Operator
33

4-
The safe navigation operator (`?`) is used to avoid a `NullPointerException` and comes
4+
The safe navigation operator (`?.`) is used to avoid a `NullPointerException` and comes
55
from the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[Groovy]
66
language. Typically, when you have a reference to an object, you might need to verify
77
that it is not `null` before accessing methods or properties of the object. To avoid
@@ -81,6 +81,64 @@ For example, the expression `#calculator?.max(4, 2)` evaluates to `null` if the
8181
`max(int, int)` method will be invoked on the `#calculator`.
8282
====
8383

84+
[[expressions-operator-safe-navigation-indexing]]
85+
== Safe Index Access
86+
87+
Since Spring Framework 6.2, the Spring Expression Language supports safe navigation for
88+
indexing into the following types of structures.
89+
90+
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-arrays-and-collections[arrays and collections]
91+
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-strings[strings]
92+
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-maps[maps]
93+
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-objects[objects]
94+
95+
The following example shows how to use the safe navigation operator for indexing into
96+
a list (`?.[]`).
97+
98+
[tabs]
99+
======
100+
Java::
101+
+
102+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
103+
----
104+
ExpressionParser parser = new SpelExpressionParser();
105+
IEEE society = new IEEE();
106+
EvaluationContext context = new StandardEvaluationContext(society);
107+
108+
// evaluates to Inventor("Nikola Tesla")
109+
Inventor inventor = parser.parseExpression("members?.[0]") // <1>
110+
.getValue(context, Inventor.class);
111+
112+
society.members = null;
113+
114+
// evaluates to null - does not throw an exception
115+
inventor = parser.parseExpression("members?.[0]") // <2>
116+
.getValue(context, Inventor.class);
117+
----
118+
<1> Use null-safe index operator on a non-null `members` list
119+
<2> Use null-safe index operator on a null `members` list
120+
121+
Kotlin::
122+
+
123+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
124+
----
125+
val parser = SpelExpressionParser()
126+
val society = IEEE()
127+
val context = StandardEvaluationContext(society)
128+
129+
// evaluates to Inventor("Nikola Tesla")
130+
var inventor = parser.parseExpression("members?.[0]") // <1>
131+
.getValue(context, Inventor::class.java)
132+
133+
society.members = null
134+
135+
// evaluates to null - does not throw an exception
136+
inventor = parser.parseExpression("members?.[0]") // <2>
137+
.getValue(context, Inventor::class.java)
138+
----
139+
<1> Use null-safe index operator on a non-null `members` list
140+
<2> Use null-safe index operator on a null `members` list
141+
======
84142

85143
[[expressions-operator-safe-navigation-selection-and-projection]]
86144
== Safe Collection Selection and Projection

framework-docs/modules/ROOT/pages/core/expressions/language-ref/properties-arrays.adoc

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ into various structures.
77
NOTE: Numerical index values are zero-based, such as when accessing the n^th^ element of
88
an array in Java.
99

10+
TIP: See the xref:core/expressions/language-ref/operator-safe-navigation.adoc[Safe Navigation Operator]
11+
section for details on how to navigate object graphs and index into various structures
12+
using the null-safe operator.
13+
1014
[[expressions-property-navigation]]
1115
== Property Navigation
1216

spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@
5858
* <li>Objects: the property with the specified name</li>
5959
* </ul>
6060
*
61+
* <h3>Null-safe Indexing</h3>
62+
*
63+
* <p>As of Spring Framework 6.2, null-safe indexing is supported via the {@code '?.'}
64+
* operator. For example, {@code 'colors?.[0]'} will evaluate to {@code null} if
65+
* {@code colors} is {@code null} and will otherwise evaluate to the 0<sup>th</sup>
66+
* color.
67+
*
6168
* @author Andy Clement
6269
* @author Phillip Webb
6370
* @author Stephane Nicoll

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

+18
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,24 @@ void nullSafePropertyAccess() {
688688
assertThat(city).isNull();
689689
}
690690

691+
@Test
692+
void nullSafeIndexing() {
693+
IEEE society = new IEEE();
694+
EvaluationContext context = new StandardEvaluationContext(society);
695+
696+
// evaluates to Inventor("Nikola Tesla")
697+
Inventor inventor = parser.parseExpression("members?.[0]") // <1>
698+
.getValue(context, Inventor.class);
699+
assertThat(inventor).extracting(Inventor::getName).isEqualTo("Nikola Tesla");
700+
701+
society.members = null;
702+
703+
// evaluates to null - does not throw an Exception
704+
inventor = parser.parseExpression("members?.[0]") // <2>
705+
.getValue(context, Inventor.class);
706+
assertThat(inventor).isNull();
707+
}
708+
691709
@Test
692710
@SuppressWarnings("unchecked")
693711
void nullSafeSelection() {

0 commit comments

Comments
 (0)