Skip to content

Spring Expression Language Syntax

Matt Magoffin edited this page Mar 22, 2023 · 3 revisions

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

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)

Cheat Sheet

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]

Literal Expressions

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 or false
  • 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.").

Operators

The Spring Expression Language supports the following kinds of operators:

  • relational — comparison operations like less-than, equal
  • ternaryif-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

Relational Operators

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 than null (X > null is always true) and no other value is ever less than null (X < null is always false).

Here are some examples:

0 > 1                 // false
0 != 1                // true
0 le 1                // true
'apple' matches '^a'  // true

Logical Operators

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

Ternary Operator

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'

Elvis Operator

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'

Mathematical Operators

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

Assignment Operator

The assignment operator in SpEL is =. Here is an example:

// save full name into `name` variable
name = firstName + ' ' + lastName

Type Operator

💡 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

Variables

The following sets of characters are allowed in variable names:

  • a - z
  • A - Z
  • 0 - 9
  • _
  • $

The name may not start with a number.

Properties

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 and person.Birthdate.Year - 1900, respectively.

Safe Navigation Operator

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

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

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'}

Collection Selection

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}

First/last Element Selection

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.

Collection Projection

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

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'
Clone this wiki locally