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:
parent
bdcd10e228
commit
fdbefad59c
|
@ -1,11 +1,21 @@
|
||||||
[[expressions-properties-arrays]]
|
[[expressions-properties-arrays]]
|
||||||
= Properties, Arrays, Lists, Maps, and Indexers
|
= Properties, Arrays, Lists, Maps, and Indexers
|
||||||
|
|
||||||
Navigating with property references is easy. To do so, use a period to indicate a nested
|
The Spring Expression Language provides support for navigating object graphs and indexing
|
||||||
property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
|
into various structures.
|
||||||
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
|
NOTE: Numerical index values are zero-based, such as when accessing the n^th^ element of
|
||||||
Pupin's city of birth, we use the following expressions:
|
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]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -16,6 +26,7 @@ Java::
|
||||||
// evaluates to 1856
|
// evaluates to 1856
|
||||||
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
|
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
|
||||||
|
|
||||||
|
// evaluates to "Smiljan"
|
||||||
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
|
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -26,6 +37,7 @@ Kotlin::
|
||||||
// evaluates to 1856
|
// evaluates to 1856
|
||||||
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
|
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
|
||||||
|
|
||||||
|
// evaluates to "Smiljan"
|
||||||
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
|
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
@ -39,8 +51,20 @@ method invocations -- for example, `getPlaceOfBirth().getCity()` instead of
|
||||||
`placeOfBirth.city`.
|
`placeOfBirth.city`.
|
||||||
====
|
====
|
||||||
|
|
||||||
The contents of arrays and lists are obtained by using square bracket notation, as the
|
[[expressions-indexing-arrays-and-collections]]
|
||||||
following example shows:
|
== 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]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -63,7 +87,8 @@ Java::
|
||||||
String name = parser.parseExpression("members[0].name").getValue(
|
String name = parser.parseExpression("members[0].name").getValue(
|
||||||
context, ieee, String.class);
|
context, ieee, String.class);
|
||||||
|
|
||||||
// List and Array navigation
|
// List and Array Indexing
|
||||||
|
|
||||||
// evaluates to "Wireless communication"
|
// evaluates to "Wireless communication"
|
||||||
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
|
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
|
||||||
context, ieee, String.class);
|
context, ieee, String.class);
|
||||||
|
@ -88,16 +113,22 @@ Kotlin::
|
||||||
val name = parser.parseExpression("members[0].name").getValue(
|
val name = parser.parseExpression("members[0].name").getValue(
|
||||||
context, ieee, String::class.java)
|
context, ieee, String::class.java)
|
||||||
|
|
||||||
// List and Array navigation
|
// List and Array Indexing
|
||||||
|
|
||||||
// evaluates to "Wireless communication"
|
// evaluates to "Wireless communication"
|
||||||
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
|
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
|
||||||
context, ieee, String::class.java)
|
context, ieee, String::class.java)
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
The contents of maps are obtained by specifying the literal key value within the
|
[[expressions-indexing-strings]]
|
||||||
brackets. In the following example, because keys for the `officers` map are strings, we can specify
|
== Indexing into Strings
|
||||||
string literals:
|
|
||||||
|
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]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -105,38 +136,113 @@ Java::
|
||||||
+
|
+
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
----
|
----
|
||||||
// Officer's Dictionary
|
// evaluates to "T" (8th letter of "Nikola Tesla")
|
||||||
|
String character = parser.parseExpression("members[0].name[7]")
|
||||||
Inventor pupin = parser.parseExpression("officers['president']").getValue(
|
.getValue(societyContext, String.class);
|
||||||
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");
|
|
||||||
----
|
----
|
||||||
|
|
||||||
Kotlin::
|
Kotlin::
|
||||||
+
|
+
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
----
|
----
|
||||||
// Officer's Dictionary
|
// evaluates to "T" (8th letter of "Nikola Tesla")
|
||||||
|
val character = parser.parseExpression("members[0].name[7]")
|
||||||
val pupin = parser.parseExpression("officers['president']").getValue(
|
.getValue(societyContext, String::class.java)
|
||||||
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")
|
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
|
[[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)
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
|
|
@ -161,60 +161,99 @@ class SpelDocumentationTests extends AbstractExpressionTests {
|
||||||
class PropertiesArraysListsMapsAndIndexers {
|
class PropertiesArraysListsMapsAndIndexers {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void propertyAccess() {
|
void propertyNavigation() {
|
||||||
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
|
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
|
||||||
|
|
||||||
|
// evaluates to 1856
|
||||||
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856
|
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856
|
||||||
assertThat(year).isEqualTo(1856);
|
assertThat(year).isEqualTo(1856);
|
||||||
|
|
||||||
|
// evaluates to "Smiljan"
|
||||||
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
|
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
|
||||||
assertThat(city).isEqualTo("Smiljan");
|
assertThat(city).isEqualTo("Smiljan");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void propertyNavigation() {
|
void indexingIntoArraysAndCollections() {
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext();
|
StandardEvaluationContext teslaContext = TestScenarioCreator.getTestEvaluationContext();
|
||||||
|
StandardEvaluationContext societyContext = new StandardEvaluationContext();
|
||||||
|
societyContext.setRootObject(new IEEE());
|
||||||
|
|
||||||
// Inventions Array
|
// Inventions Array
|
||||||
|
|
||||||
// evaluates to "Induction motor"
|
// evaluates to "Induction motor"
|
||||||
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class);
|
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class);
|
||||||
assertThat(invention).isEqualTo("Induction motor");
|
assertThat(invention).isEqualTo("Induction motor");
|
||||||
|
|
||||||
// Members List
|
// Members List
|
||||||
StandardEvaluationContext societyContext = new StandardEvaluationContext();
|
|
||||||
societyContext.setRootObject(new IEEE());
|
|
||||||
|
|
||||||
// evaluates to "Nikola Tesla"
|
// evaluates to "Nikola Tesla"
|
||||||
String name = parser.parseExpression("members[0].Name").getValue(societyContext, String.class);
|
String name = parser.parseExpression("members[0].Name").getValue(societyContext, String.class);
|
||||||
assertThat(name).isEqualTo("Nikola Tesla");
|
assertThat(name).isEqualTo("Nikola Tesla");
|
||||||
|
|
||||||
// List and Array navigation
|
// List and Array Indexing
|
||||||
|
|
||||||
// evaluates to "Wireless communication"
|
// evaluates to "Wireless communication"
|
||||||
invention = parser.parseExpression("members[0].Inventions[6]").getValue(societyContext, String.class);
|
invention = parser.parseExpression("members[0].Inventions[6]").getValue(societyContext, String.class);
|
||||||
assertThat(invention).isEqualTo("Wireless communication");
|
assertThat(invention).isEqualTo("Wireless communication");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void maps() {
|
void indexingIntoStrings() {
|
||||||
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
StandardEvaluationContext societyContext = new StandardEvaluationContext();
|
StandardEvaluationContext societyContext = new StandardEvaluationContext();
|
||||||
societyContext.setRootObject(new IEEE());
|
societyContext.setRootObject(new IEEE());
|
||||||
// Officer's map
|
|
||||||
Inventor pupin = parser.parseExpression("officers['president']").getValue(societyContext, Inventor.class);
|
// evaluates to "T" (8th letter of "Nikola Tesla")
|
||||||
|
String character = parser.parseExpression("members[0].name[7]")
|
||||||
|
.getValue(societyContext, String.class);
|
||||||
|
assertThat(character).isEqualTo("T");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void indexingIntoMaps() {
|
||||||
|
StandardEvaluationContext societyContext = new StandardEvaluationContext();
|
||||||
|
societyContext.setRootObject(new IEEE());
|
||||||
|
|
||||||
|
// Officer's Map
|
||||||
|
|
||||||
|
// evaluates to Inventor("Pupin")
|
||||||
|
Inventor pupin = parser.parseExpression("officers['president']")
|
||||||
|
.getValue(societyContext, Inventor.class);
|
||||||
assertThat(pupin).isNotNull();
|
assertThat(pupin).isNotNull();
|
||||||
|
assertThat(pupin.getName()).isEqualTo("Pupin");
|
||||||
|
|
||||||
// evaluates to "Idvor"
|
// evaluates to "Idvor"
|
||||||
String city = parser.parseExpression("officers['president'].PlaceOfBirth.city").getValue(societyContext, String.class);
|
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
|
||||||
assertThat(city).isNotNull();
|
.getValue(societyContext, String.class);
|
||||||
|
assertThat(city).isEqualTo("Idvor");
|
||||||
|
|
||||||
|
String countryExpression = "officers['advisors'][0].placeOfBirth.Country";
|
||||||
|
|
||||||
// setting values
|
// setting values
|
||||||
Inventor i = parser.parseExpression("officers['advisors'][0]").getValue(societyContext, Inventor.class);
|
parser.parseExpression(countryExpression)
|
||||||
assertThat(i.getName()).isEqualTo("Nikola Tesla");
|
.setValue(societyContext, "Croatia");
|
||||||
|
|
||||||
parser.parseExpression("officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia");
|
// evaluates to "Croatia"
|
||||||
|
String country = parser.parseExpression(countryExpression)
|
||||||
Inventor i2 = parser.parseExpression("reverse[0]['advisors'][0]").getValue(societyContext, Inventor.class);
|
.getValue(societyContext, String.class);
|
||||||
assertThat(i2.getName()).isEqualTo("Nikola Tesla");
|
assertThat(country).isEqualTo("Croatia");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void indexingIntoObjects() {
|
||||||
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
assertThat(name).isEqualTo("Nikola Tesla");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|
Loading…
Reference in New Issue