Improve documentation for SpEL indexing support

Prior to this commit, the reference manual only documented indexing
support for arrays, lists, and maps.

This commit improves the overall documentation for SpEL's property
navigation and indexing support and introduces additional documentation
for indexing into Strings and Objects.

Closes gh-32355
This commit is contained in:
Sam Brannen
2024-03-01 18:08:10 +01:00
parent bdcd10e228
commit fdbefad59c
2 changed files with 197 additions and 52 deletions

View File

@@ -1,11 +1,21 @@
[[expressions-properties-arrays]]
= Properties, Arrays, Lists, Maps, and Indexers
Navigating with property references is easy. To do so, use a period to indicate a nested
property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
populated with data listed in the xref:core/expressions/example-classes.adoc[Classes used in the examples]
section. To navigate "down" the object graph and get Tesla's year of birth and
Pupin's city of birth, we use the following expressions:
The Spring Expression Language provides support for navigating object graphs and indexing
into various structures.
NOTE: Numerical index values are zero-based, such as when accessing the n^th^ element of
an array in Java.
[[expressions-property-navigation]]
== Property Navigation
You can navigate property references within an object graph by using a period to indicate
a nested property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
populated with data listed in the
xref:core/expressions/example-classes.adoc[Classes used in the examples] section. To
navigate _down_ the object graph and get Tesla's year of birth and Pupin's city of birth,
we use the following expressions:
[tabs]
======
@@ -16,6 +26,7 @@ Java::
// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
// evaluates to "Smiljan"
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
----
@@ -26,6 +37,7 @@ Kotlin::
// evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
// evaluates to "Smiljan"
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
----
======
@@ -39,8 +51,20 @@ method invocations -- for example, `getPlaceOfBirth().getCity()` instead of
`placeOfBirth.city`.
====
The contents of arrays and lists are obtained by using square bracket notation, as the
following example shows:
[[expressions-indexing-arrays-and-collections]]
== Indexing into Arrays and Collections
The n^th^ element of an array or collection (for example, a `Set` or `List`) can be
obtained by using square bracket notation, as the following example shows.
[NOTE]
====
If the indexed collection is a `java.util.List`, the n^th^ element will be accessed
directly via `list.get(n)`.
For any other type of `Collection`, the n^th^ element will be accessed by iterating over
the collection using its `Iterator` and returning the n^th^ element encountered.
====
[tabs]
======
@@ -63,7 +87,8 @@ Java::
String name = parser.parseExpression("members[0].name").getValue(
context, ieee, String.class);
// List and Array navigation
// List and Array Indexing
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class);
@@ -88,16 +113,22 @@ Kotlin::
val name = parser.parseExpression("members[0].name").getValue(
context, ieee, String::class.java)
// List and Array navigation
// List and Array Indexing
// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java)
----
======
The contents of maps are obtained by specifying the literal key value within the
brackets. In the following example, because keys for the `officers` map are strings, we can specify
string literals:
[[expressions-indexing-strings]]
== Indexing into Strings
The n^th^ character of a string can be obtained by specifying the index within square
brackets, as demonstrated in the following example.
NOTE: The n^th^ character of a string will evaluate to a `java.lang.String`, not a
`java.lang.Character`.
[tabs]
======
@@ -105,38 +136,113 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Officer's Dictionary
Inventor pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String.class);
// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia");
// evaluates to "T" (8th letter of "Nikola Tesla")
String character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Dictionary
val pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor::class.java)
// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String::class.java)
// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia")
// evaluates to "T" (8th letter of "Nikola Tesla")
val character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String::class.java)
----
======
[[expressions-indexing-maps]]
== Indexing into Maps
The contents of maps are obtained by specifying the key value within square brackets. In
the following example, because keys for the `officers` map are strings, we can specify
string literals such as `'president'`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Officer's Map
// evaluates to Inventor("Pupin")
Inventor pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String.class);
String countryExpression = "officers['advisors'][0].placeOfBirth.country";
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia");
// evaluates to "Croatia"
String country = parser.parseExpression(countryExpression)
.getValue(societyContext, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Map
// evaluates to Inventor("Pupin")
val pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor::class.java)
// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String::class.java)
val countryExpression = "officers['advisors'][0].placeOfBirth.country"
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia")
// evaluates to "Croatia"
val country = parser.parseExpression(countryExpression)
.getValue(societyContext, String::class.java)
----
======
[[expressions-indexing-objects]]
== Indexing into Objects
A property of an object can be obtained by specifying the name of the property within
square brackets. This is analogous to accessing the value of a map based on its key. The
following example demonstrates how to _index_ into an object to retrieve a specific
property.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Create an inventor to use as the root context object.
val tesla = Inventor("Nikola Tesla")
// evaluates to "Nikola Tesla"
val name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String::class.java)
----
======