SpEL Syntax
The tSM Spring Expression Language (SpEL) couples a clean, readable syntax with the full power of Java, giving you an expressive way to automate anything inside the tSM platform.
This page is your one-stop reference: a quick tour of every major SpEL concept and a thorough specification of every syntax rule, console shortcut and best-practice pattern.
1 Running scripts
1.1 SpEL Console Usage & Shortcuts
1.1.1 Opening the Console
Mode | How to open | When to use |
---|---|---|
Without context | In the main left menu search for spel and select SpEL Console. | Quick experiments, standalone utilities. |
With context | Open an entity (order, ticket, …), click the ⋯ menu in the top-right corner and choose SpEL Console. | When you need context variables such as #order or #ticket . |
1.1.2 Autocomplete Cheat-sheet
Shortcut | Action |
---|---|
Ctrl + Space | Open autocomplete suggestions. |
Double Ctrl + Space | Show a description of the highlighted suggestion. |
Ctrl + Enter | Evaluate the entire script. |
Select text + Ctrl + Enter | Evaluate only the selected expression. |
Select documentation + Enter | Insert example snippet of selected parameter. |
@ then Ctrl + Space | List all tSM services. |
# then Ctrl + Space | List standalone functions, variables and flow operators. |
Enter inside () | Insert example arguments for a method. |
1.1.3 Running Scripts & Expressions
Ctrl + Enter
– evaluate the whole console buffer.- Select +
Ctrl + Enter
– evaluate only the highlighted expression (one at a time).
1.2 Example Script Structure — Multiple Expressions
// Example script – write as a whitespace-separated list of expressions
#orderTotal = 250
#discountedTotal = #orderTotal - 50
#date = #now().formatted('dd-MM-yyyy HH:mm')
#user = @user.user.get(#currentUser()).name
#orderSummary = 'Price: ' + #discountedTotal + '; Date: ' + #date + '; User: ' + #user
Hint Most snippets in this guide can be pasted straight into the SpEL console. Use the little icon that appears in the top-right corner of each block.
Note A script is simply one or more expressions separated by whitespaces. Result of entire script is the result of the last command.
Rule of thumb: Any syntactically valid expression that returns exactly one value—be it a number, a string, a map or a whole object graph—counts as one expression.
2 SpEL Syntax Reference
2.1 Literals and Their Evaluation
// Literals
'Hello World' // String
1234 // Decimal number
123.4 // Floating-point number
0x7FFFFFFF // Hexadecimal
6.0221415E+23 // Scientific notation
true // Boolean
null // Null
2.2 Variable Assignment
Assigning values to variables for later use in expressions. Operator: =
#myVariable = 'Some Value'
#anotherVariable = 42
#user = #currentUser()
2.3 Expression Templating
Combining literals and variables to create dynamic strings. Operator: +
#name = 'John Doe'
#greeting = 'Hello, ' + #name + '!'
2.4 Data Types
Different types of data that can be used in tSM SpEL. Commonly used data types are:
#myString = 'Hello world!' // String
#myInteger = 123 // Integer
#myDouble = 123.45 // Double
#myBoolean = true // Boolean
#myList = ['order', 'ticket', 'process'] // List
#myMap = {'customerName':'Alpha', 'employees':100} // Map
#myObject = @user.user.get(#currentUser()) // Object
#myJsonNode = '{"name":"John","age":30}'.toJsonNode() // JsonNode
#mySpreadsheet = #spreadsheet // Spreadsheet
#myDate = #now() // Date
#myUUID = #randomUUID() // UUID
#myNull = null // Null
Note Under the hood, tSM SpEL uses Java and Kotlin. For example,
String
is a JavaString
,List
is a JavaList
, and so on. For security reasons, you cannot use any Java class or method directly in SpEL. We only allow a limited set of classes and methods that are safe to use in the tSM environment. However, general rules of Java apply, so you can consult Java and Kotlin documentation for more details on behaviour of standard classes and methods.
2.5 Basic Operations & Operators
2.5.1 Arithmetic
Used to perform arithmetic operations. Operators available: +
, -
, *
, ^
, /
or div
, %
or mod
#addition = 1 + 2 // 3
#subtraction = 5 - 2 // 3
#multiplication = 3 * 4 // 12
#exponentiation = 2 ^ 3 // 8
#division = 10 / 3 // 5 —or— 10 div 2
#modulus = 10 % 3 // 1 —or— 10 mod 3
2.5.2 Relational
Used to compare two values. Operators available: ==
or eq
, !=
or ne
, <
or lt
, >
or gt
, <=
or le
, >=
or ge
#isEqual = 1 == 1 // true —or— 1 eq 1
#isNotEqual = 1 != 2 // true —or— 1 ne 2
#isLessThan = 1 < 2 // true —or— 1 lt 2
#isGreaterThan = 2 > 1 // true —or— 2 gt 1
#isLessThanOrEqual = 1 <= 1 // true —or— 1 le 1
#isGreaterThanOrEqual= 2 >= 1 // true —or— 2 ge 1
#isEqualString = 'is' == 'is'
#isNotEqualString = 'is' != 'not'
2.5.3 Logical
Used to perform logical operations. Operators available: &&
or and
, ||
or or
, !
or not
#logicalAnd = true && false // false —or— true and false
#logicalOr = true || false // true —or— true or false
#logicalNot = !true // false —or— not true
2.6 Data Access
2.6.1 Property / Key Access
Accessing values in 'Maps' and 'Objects' by their 'Keys' or 'Properties'. Operators: .
, ['key']
#myMap = {'name':'Your Company', 'address':{'street':'Na padesátém','city':'Prague'}}
#myMap.name // "Your Company" —or— #myMap['name']
#myMap.address.street // "Na padesátém" —or— #myMap['address']['street']
2.6.2 List Item Access
Accessing 'Items' in a 'List' by their index. Operator: [index]
#myList = {'order', 'task', 'process'}
#myList[0] // "order"
2.6.3 Collection Selection & Projection
Collection Selection: Selecting items from a collection based on specific criteria. Operator: .?[condition]
Collection Projection: Projecting specific properties of items in a collection. Operator: .![key]
// Collection Selection and Collection Projection
// You have this 'List of Maps', for example:
#services = [{'name': 'Int', 'price': 10}, {'name': 'TV', 'price': 15}, {'name': 'Phone', 'price': 5}]
// Selecting services with price greater than 9
#sel = #services.?[price > 9] // [{'name': 'Int', 'price': 10}, {'name': 'TV', 'price': 15}]; Collection Selection
// Projecting the names of the selected services
#serviceNames = #sel.![name] // ['Int', 'TV']; Collection Projection
// Or you can combine them in one step
#selectedServiceNames = #services.?[price > 10].![name] // Combined selection and projection
#selectedServiceNames // ['TV']
Tip As the result of the script is the last expression, to evaluate only #sel, select the script up to the row and hit
Ctrl + Enter
. Or you can use debugger to see partial results.
2.7 Flow Control & Ternary Operator
2.7.1 Condition-based Flow Control
Controlling the flow of execution in expressions.
Condition-based flow control operators allow you to execute expressions based on specific conditions, providing a way to handle different scenarios dynamically. Operators available:
#if(cond1).then(exp1).elseif(cond2).then(exp2).else(exp3)
#match(var1).when(valueOfVar1,exp1).else(exp2)
#case().when(cond1,exp1).else(exp2)
// If-then-elseif-then-else Flow Control
#total = 99
#if(#total > 200)
.then(#discount = 0.20)
.elseif(#total > 100)
.then(#discount = 0.10 )
.else(#discount = 0.05 ) // 0.05
// Match-when-else Flow Control
#technologyAvailable = '5G'
#match(#technologyAvailable)
.when('5G',#provision = 'mobile')
.else(#provision = 'fix') // 'mobile'
2.7.2 Block-based Flow Control
Block-based flow control operators allow you to group expressions and execute them together, making it easier to manage complex logic. Operators available:
#with(exp1,exp2).do(exp3)
#do(exp1, exp2)
#with(
#orderTotal = 250,
#orderDiscount = 0.10,
#discountedTotal = #orderTotal - (#orderTotal * #orderDiscount)
).do(
#orderType = #case()
.when(#orderTotal > 100, 'Large Order')
.else('Small Order')
) // 'Large Order'
2.7.3 Iteration
Iteration operators allow you to loop through collections and execute expressions for each item, enabling repetitive tasks to be handled efficiently.
Available operator:
.forEach(var1, exp1, exp2, ...)
var1
– the name of the variable that will hold each item during the iterationexpN
– one or more expressions to evaluate for each item
Note: During the loop, a special variable #index
is also available. This represents the zero-based index of the current item within the collection.
Example 1: Calculating squares
#numbers = [1, 2, 3, 4]
#squares = new java.util.ArrayList()
#numbers.forEach(#num, #squares.add(#num * #num))
#squares // [1, 4, 9, 16]
Example 2: Adding indexed strings
#fruits = ['apple', 'banana', 'cherry']
#results = new java.util.ArrayList()
#fruits.forEach(#item, #results.add('Item ' + #index + ': ' + #item))
#results // ['Item 0: apple', 'Item 1: banana', 'Item 2: cherry']
2.7.4 Ternary
The ternary operator is a concise way to perform conditional logic in place of an if-then-else statement. It simplifies the code and enhances readability, especially for simple conditions.
#orderTotal = 150
#orderType = #orderTotal > 100 ? 'Large Order' : 'Small Order' // 'Large Order'
2.8 Null Safety & Error handling
2.8.1 Null Safety Operator
The null safety operator helps prevent NullPointerExceptions by allowing safe navigation through potential null references.
// Safe navigation through potential null references - Null Safety Operator
#order = {'customer': null}
#customerName = #order.customer?.name // null if customer is null
2.8.2 Default Value Setting - Elvis Operator
The Elvis operator provides a shorthand for assigning default values when expressions evaluate to null.
// Default value assignment using Elvis Operator
#customerName = null
#displayName = #customerName ?: 'Unknown Customer' // 'Unknown Customer'
2.8.3 Error Catching
The try-catch construct allows handling exceptions within SpEL expressions.
// Exception handling using Try-catch Flow Control
#result = #try(
#riskyOperation()
).catch(
'Error occurred'
)
2.9 Built-in Functions & Type Extensions
There are two kinds of built-in functions:
-
Type extensions – methods that extend everyday data types such as strings, numbers, dates, lists, sets and maps. They read naturally:
'Hello'.uppercase() // "HELLO" [1,2,3].sum() // 6 (example only)
-
Standalone functions – utilities that are not tied to any specific value and are invoked with the
#
prefix:#now() // current date-time #randomUUID() // random unique ID e.g. 'aee4b129-0680-4aff-8b7a-af2e83472336' #if(#now().formatted('HH') == '18').then('Yes').else('No') // 'Yes' if it's currently 18:00 to 18:59, else 'No'
See 07_SpEL_builtin_functions.md
for the full catalogue.
2.10 Context Variables
Context variables provide access to various aspects and provide informarmation about the current execution context in tSM.
// Context Variables
#orderProductCodes = #order.productCodes() // Get list of product codes processed within order
#completedTask = #task.complete() // Finish the current task and move process forward
#processInfo = #execution.processInstance // Get information about running Process
#ticketStatus = #ticket.status // Retrieves status of current running ticket
2.11 tSM Services
tSM services are powerful tools that allow you to interact with various functionalities within the tSM platform. Using them you can manipulate every entity of the tSM platform, create various API calls and use them as part of integration with another platfrom. To these there are about 1500 methods of tSM services available, so they can't be listed here, but we will look at them deeper in chapter III. Standard syntax of tSM Service is @tsmService.tsmMethod
.
#currentUser = @user.user.get(#currentUser()).name // Your full name, e.g.: 'Adam Sandler'
#customer = @tsmDatabaseClient
.query("select id,key from crm.crmt_customer")
.execute()
.first()
.id // Customers UUID, e.g 'f05bd47a-f43a-44e1-bf1e-79823eeff891'
#account = @crm.person.find({customerId: #customer}).first().phones // Phones of person associated with customer
Hint: To explore tSM Services, you can leverage the autocomplete option of the tSM Console or open the 'tSM Public API' item in the main menu of the tSM Platform. Here, you will find the Swagger documentation for all available services, as well as Public API operations.
2.13 Advanced Expressions & Constructs
2.13.1 Class Expressions
Class expressions allow you to access static methods and fields of a class directly within SpEL.
#sqrtResult = T(java.lang.Math).sqrt(16) // 4
2.13.2 Regular Expressions
#matches = '192.168.0.2'
.matches('^(25[0-5]|2[0-4][0-9]|1\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4][0-9]|1\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4][0-9]|1\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4][0-9]|1\d{2}|[1-9]?\d)$')
// true
3 Complete Script Example
// Primitive values
#orderTotal = 250
#orderDiscount = 0.10
#orderStatus = 'Pending'
#notificationSent = false
#priority = false
#customerName = 'John Doe'
// Collections
#adminEmails = ['admin1@telco.com', 'admin2@telco.com']
#services = [
{'name':'Internet','price':100},
{'name':'TV','price':150},
{'name':'Phone','price':50}
]
// Calculations
#discountedTotal = #orderTotal - (#orderTotal * #orderDiscount)
#isLargeOrder = #orderTotal > 100
// Ternary & Elvis
#orderType = #isLargeOrder ? 'Large Order' : 'Small Order'
#isPriority = #isLargeOrder ? true : false
#customerNameSafe = #customerName ?: 'Unknown Customer'
// Collection ops
#selectedServices = #services.?[price > 50]
#serviceNames = #services.![name]
// Flow control & service call
#sendNotification = #if(#isPriority and !#notificationSent).then(
#adminEmails.forEach(
#email,
@notificationPublicService.sendNotification({
"templateCode": "TaskAssigned",
"ownerId": #currentUser(),
"ownerType": "Order",
"notificationTo": [
{"ownerId": #currentUser(), "ownerType": "User"},
{"email" : "peter.yan@google.com"}
],
"data": { "order": #order, "task": #task }
})
),
#notificationSent = true
).else('Notification not needed')
#result = {
'Total Amount' : #orderTotal.toString(),
'Discounted Total' : #discountedTotal.toString(),
'Order Status' : #orderStatus,
'Order Type' : #orderType,
'Priority Order' : #isPriority,
'Customer Name' : #customerNameSafe,
'Services' : #serviceNames.toString(),
'Selected Services' : #selectedServices.toString(),
'Notification Sent' : #notificationSent
}