-
Notifications
You must be signed in to change notification settings - Fork 2
Spring Expression Language Syntax
The Spring Expression Language (SpEL or spel
) is a general purpose language suited for reading
data properties and performing simple operations using a simple syntax. This guide is based on the
official language reference, simplified for SolarNetwork users of the language.
This guide will detail just the syntax of the SpEL language. It will not provide information about what properties or functions are available to your expressions. See the SolarNode Expressions guide for information about the properties and functions available in SolarNode.
- Introduction — brief overview
- Cheat Sheet — quick overview of main language synax
-
Literal Expressions — strings, numbers, boolean,
null
-
Operators
- relational — comparison operations like less-than, equal
-
ternary — if-then-else expression
- Elvis — not-null-else ternary expression
- logical — conditional boolean operations like and, or
- mathematical — math operations like addition, multiplication
- assignment — saving a value into a property or variable
- type — low-level access to a class
- Variables — variable name rules
-
Properties — accessing properties and functions on objects
-
Safe Navigation Operator —
null
-safe property accessor
-
Safe Navigation Operator —
- Lists — ordered collection of objects
- Maps — collection of key/value associations
-
Collection Selection — filtering lists and maps
- First/last Element Selection — extracting the first or last element of a collection
- Collection Projection — derive a new collection using a collection selection
- Functions — invoking functions
- Types — access to classes
SpEl uses a syntax that has similarities to both JavaScript and Java. Here are some simple examples:
// multiply a property by 1000
watts * 1000
// join a first and last name
firstName + " " + lastName
// if-then-else comparison
frequency > 49.5 && frequency < 50.5 ? "ok" : "out-of-bounds"
// call a min() helper function
min(phase_a, phase_b, phase_b)
Here is a quick overview of the main SpEL language syntax:
// strings use single or double quotes, doubled to escape
'a string', "another string", 'here''s a string, too'
// string concatenation with +
'a string' + ' ' + 'another string'
// numbers can be integers, hex, floating point, exponential
123, 0x7b, 1.23, 123E-2
// addition, subtraction, multiplication, division, modulus, exponential
+ - * /,div %,mod ^
// equal, not-equal, less-than, less-than-or-equal, greater-than, greater-than-or-equal
== != < <= > >=
eq ne lt le gt ge
// string regex matching
'a string' matches '^a'
// logical and, or, not
&& || !
and or not
// ternary if-then-else, Elvis shortcut
val > 0 ? 'positive' : 'not positive'; name ?: 'Unknown'
// property navigation, safe navigation
obj.prop; obj?.prop
// lists, maps
{1,2,3,4}; {name:'Foo',color:'Red'}
// list, map selection
list.?[prop > 1]; map.?[key < 5]
// list, map projection
list.![address.city]; map.![value.name]
SpEL supports the following types of literal expressions.
-
strings: enclosed in single (
'
) or double ("
) quotation marks -
numbers: integer (
123
), hexadecimal (0xF1
), real (1.23
); negative (-1
) and exponential notation (1E+3
) are supported -
boolean values:
true
orfalse
-
null:
null
☝️ To include a single quotation mark within a string literal enclosed in single quotation marks, use two adjacent single quotation mark characters (
'Here''s a string.'
). Similarly, to include a double quotation mark within a string literal enclosed in double quotation marks, use two adjacent double quotation mark characters ("The forecast is ""windy"" tomorrow."
).
The Spring Expression Language supports the following kinds of operators:
- relational — comparison operations like less-than, equal
- ternary — if-then-else expression
- logical — conditional boolean operations like and, or
- mathematical — math operations like addition, multiplication
- assignment — saving a value into a property or variable
- type — low-level access to a class
SpEL supports the following relational operators:
Operator | Description |
---|---|
== , eq
|
Equal comparison |
!= , ne
|
Not-equal comparison |
< , lt
|
Less-than comparison |
<= , le
|
Less-than-or-equal comparison |
> , gt
|
Greater-than comparison |
>= , ge
|
Greater-than-or-equal comparison |
matches |
String regular expression comparison |
☝️ Greater-than and less-than comparisons against
null
follow a simple rule:null
is treated as nothing (not as zero). As a consequence, any other value is always greater thannull
(X > null
is alwaystrue
) and no other value is ever less thannull
(X < null
is alwaysfalse
).
Here are some examples:
0 > 1 // false
0 != 1 // true
0 le 1 // true
'apple' matches '^a' // true
SpEL supports the following logical operators:
Operator | Description |
---|---|
&& , and
|
Logical and test |
|| , or
|
Logical or test |
! , not
|
Logical inversion |
Here are some examples:
(0 > 1) && (0 != 1) // false
(0 > 1) || (0 != 1) // true
!(0 > 1) // true
The ternary operator if ? then : else
provides if-then-else conditional logic inside the
expression. The if expression is evaluated as a boolean; if it is true
the then expression
is evaluated, otherwise the else expression is evaluated. For example:
0 > 1 ? 'what?' : 'ok' // 'ok'
The Elvis operator (?:
, named for the resemblance to Elvis' hair style) is a shortcut version of
the ternary operator, when the if clause is a test comparing a variable to null
. Using the Elvis
operator the then clause is omitted and becomes the value of the variable from the if clause.
To illustrate how it works, take this normal ternary operator expression:
name != null ? name : 'Unknown' // evaluate to `name` if not null, 'Unknown' otherwise
Using the Elvis operator this can be shortened to:
name ?: 'Unknown'
SpEL supports the following mathematical operators:
Operator | Description |
---|---|
+ |
Addition (:warning: also used to join strings) |
- |
Subtraction |
* |
Multiplication |
/ , div
|
Division |
% , mod
|
Modulus (remainder after integer division) |
^ |
Exponential power |
Here are some examples:
1 + 2 // 3
'a' + 'b' // 'ab'
1 + 2 * 3 // 7
(1 + 2) * 3 // 9
1 / 2 // 0.5
3 % 2 // 1
2 ^ 3 // 8
The assignment operator in SpEL is =
. Here is an example:
// save full name into `name` variable
name = firstName + ' ' + lastName
💡 The type operator is an advanced feature that assumes low-level Java language knowledge.
You can use the T()
operator to specify an instance of a Java type. Static properties and methods
are accessed by using this operator as well. References to types within the java.lang
package do
not need to be fully qualified, but all other type references must be.
Here are some examples of the type operator:
T(Math).PI // 3.14159265358979323846
T(java.time.Instant).now() // current system time
The following sets of characters are allowed in variable names:
a - z
A - Z
0 - 9
_
$
The name may not start with a number.
Navigating the properties (and functions) of an object is done with the .
character.
For example, imagine the variable person
holds an object with properties like this, expressed
in JSON:
{
"firstName" : "Honeypuff",
"lastName" : "Snowtouch",
"birthdate" : {
"day" : 1,
"month" : 2,
"year" : 1132
},
"luckyNumbers" : [0, 7, 13]
}
Using that, here are some example expressions:
person.firstName + ' ' + person.lastName // 'Honeypuff Snowtouch'
person.birthdate.year - 1900 // -768
☝️ Case insensitivity is allowed for the first letter of property names. Thus, the expressions in the above example may be written as
person.FirstName + ' ' + person.LastName
andperson.Birthdate.Year - 1900
, respectively.
The safe navigation operator ?.
is used to avoid a exceptions that occur when trying to access the
property of a null
value. Typically, when you have a reference to an object, you might need to
verify that it is not null before accessing methods or properties of the object. To avoid this, the
safe navigation operator terminates any further navigation and returns null
.
To illustrate, here is a null
-safe example expression using the same person
object as in the previous
section:
persion.birthdate != null && person.birthdate.year != null ? person.birthdate.year : null
That can be shortened with the ?.
operator to:
person?.birthdate?.year
Lists can be expressed literally using {}
notation, using a ,
delimiter. Nested lists are
supported. For example:
{1, 2, 3, 4}
{{1, 2}, {3, 4}}
Maps can be expressed literally using {key:value}
notation, using a ,
delimiter. Nested maps
are supported. The key values do not need to be quoted, in less they include a .
character. For
example:
{name:'Nikola', dob:'10-July-1856'}
{name:{first:'Nikola', last:'Tesla'}, dob:{day:10, month:'July', year:1856}}
{'1.1':'Section 1.1', '1.2':'Section 1.2'}
Lists and maps can be filtered using the .?[criteria]
selection operator, where criteria
is a
selection expression. The selection expression is applied to each element of the collection. Those
that match the criteria are included in the result, which is a new collection of the matching
elements.
To illustrate list selection, imagine a list of People objects in a variable people
as shown in
the Properties section, each of which have different names and birthday values. You
could use the selection operator like this:
// get all 21st century people
people.?[birthdate.year >= 2000 && birthdate.year < 3000]
// get all people with last names starting with 'M'
people.?[lastName.startsWith('M')]
Map selection elements are entries of the map, which are objects with key
and value
properties. To illustrate map selection, imagine a map in a variable codes
like this, expressed in
JSON:
{
"a" : 1,
"b" : 2,
"z" : 26
}
Some map selection examples are:
codes.?[key != 'z'] // {a:1, b:2}
codes.?[value > 2] // {z:26}
The first or last element of a collection can be returned using the .^[criteria]
and
.$[criteria]
operators. For example:
people.^[birthdate.year < 2000].firstName // name of first person born before 2000
codes.$[value > 1].value // last map value greater than 1
☝️ Note that selection occurs in iteration-order of the collection being selected upon. Be careful to understand how the collection is ordered if using the first/last element operator.
Lists and maps can be derived from another list or map using the .![projection]
syntax, where
projection
is an expression applied to each element of the collection. The result of each
projection
expression is returned in a new list.
To illustrate, imagine a list of People objects in a variable people
as shown in
the Properties section. Here are some example projections:
people.![firstName + ' ' + lastName] // list of names
people.![birthdate.year] // list of birth years
Functions are invoked by specifying the function name followed by ()
. If the function accepts
arguments those arguments must be provided within the ()
as a comma-delimited list of literals
or variable names.
Here are some examples:
min(1,2) // 1
'hi'.toUpperCase() // 'HI'