Expressions
Overview
Many processor properties take an expression. An expression is a short query that selects a value from the message being processed, such as the key of an object to look up. Utilihive compiles each expression when the integration flow deploys, and evaluates it for each message. The processor documentation marks every property that takes an expression.
An expression is written in one of the two expression languages:
-
JSONiq, the query language of the map processor. JSONiq is the preferred language for new expressions.
-
The Spring Expression Language (SpEL). SpEL remains the default language for backwards compatibility, but JSONiq is recommended for new expressions.
Language Prefix
An expression that starts with the language prefix jsoniq: is a JSONiq expression.
An expression without the prefix is a SpEL expression.
Utilihive ignores whitespace before the prefix.
The prefix is case-sensitive, so JSONiq: does not select JSONiq.
The following example reads a payload field in JSONiq:
setPayload {
id = "set-message"
source = "jsoniq:#input.envelope.payload.myValue"
}
The same expression in SpEL is envelope.payload.myValue.
An expression that does not parse fails the deployment of its integration flow.
Standard Expression Context
An expression reads its data from an expression context. Most processor properties use the standard expression context, which holds the message envelope and its exchange properties:
| JSONiq path | SpEL path | Type | Description |
|---|---|---|---|
|
|
Any |
The payload of the message. Typically, a JSON compliant object. |
|
|
String |
The unique identifier of the message exchange. Generated by the server. |
|
|
String |
The original identifier of the message exchange from when it was initially created. This might differ from the current ID if the message has passed through a processor that generates new exchanges, such as the |
|
|
Object |
The message exchange stash. |
|
|
String |
The ID of the flow the message is currently being processed in. |
|
|
String |
The ID of the organization that owns the flow. |
|
|
Array of objects |
The message’s exchange properties, each an object with a |
|
|
Object |
The message’s exchange properties, as a map from each property name to its string value. |
Context Properties
Some processor properties use an extended context.
An extended context adds context properties to the standard expression context.
A JSONiq expression reads a context property directly under #input, as in #input.targetFlowId.
A SpEL expression reads it by its name, as in targetFlowId.
| Processor property | Context property | Description |
|---|---|---|
|
|
The ID of the target flow being evaluated. |
|
|
The list element being grouped. |
|
|
The name of the file being written. |
|
|
The position in the flow of the processor whose payload is archived, or |
|
The name of the processor whose payload is archived, with characters that are unsafe in a path replaced by |
|
|
|
|
|
The configuration of the processor whose payload is archived. Only a SpEL expression can read it. |
JSONiq Expressions
A JSONiq expression is a JSONiq query. The Data Mapping guide teaches the language.
Input
A JSONiq expression reads the expression context as the variable #input.
So #input.envelope.payload reads the payload, and #input.targetFlowId reads a context property.
$input is the same variable.
In a Kotlin string, write #input, because $ starts a string template.
A JSONiq string takes double quotes.
A Kotlin raw string, between """ and """, holds them without escaping.
For example, """jsoniq:#input.envelope.payload.value = "testValue" """ compares a payload field with the string testValue.
The expression context reads as JSON data, with the paths in the standard expression context. An expression can read this data but cannot change it.
Payload
A JSONiq expression reads the payload as the processor holds it:
| Payload | Value in a JSONiq expression |
|---|---|
The same data. |
|
A string |
A string, even when it holds JSON or XML. Convert the payload with an inboundTransformationStrategy to read its fields, or parse it in the query, as in |
Reading it fails the expression, with an error saying that the payload is a stream. An expression that does not read the payload runs normally, and the stream stays untouched. |
|
Reading it fails the expression, with an error naming the payload’s type. |
Names and Arrays
A name that holds a dot takes double quotes, as in #input.exchangePropertiesMap."http.header.x-correlation-id".
JSONiq reads an unquoted dot as a step into an object, so the unquoted name returns an empty result.
A name that holds a hyphen, such as x-fileid, needs no quotes.
Array positions start at 1 in JSONiq.
So #input.envelope.payload.readings[[1]] reads the first element of an array, which SpEL reads as envelope.payload.readings[0].
The Data Mapping guide explains arrays and sequences.
Paths in the Map Processor
The map processor binds the envelope itself as #input.
A map processor’s mapSpec therefore reads the payload as #input.payload.
A JSONiq expression gets the whole expression context as #input, so the envelope sits one level down, at #input.envelope.
Below the envelope, the paths are the same:
| Value | Map processor | JSONiq expression | SpEL expression |
|---|---|---|---|
A payload field |
|
|
|
A stash entry |
|
|
|
An exchange property |
|
|
|
Results
Each processor property asks for a type of value, such as a boolean or a string.
A JSONiq expression must return a value of that type, because nothing converts its result.
So the string "false" is not a boolean, and the number 42 is not a string.
| Result | Outcome |
|---|---|
The empty sequence, an empty array, or |
An empty result. The expression fails with a "No data found" error where the property requires a value. |
A string or a boolean |
Returned where the property asks for that type, or for any value. |
A number |
Returned where the property asks for a number type that holds the value exactly, or for any value. An integer property accepts |
An object |
Returned where the property asks for any value. |
A non-empty array, or a sequence of several items |
Returned as a list where the property asks for any value. |
Every other result fails the expression.
The error names the expression and the value it returned, as in returned the number 42, not a value of type String.
Write any conversion in the query itself:
-
string(#input.envelope.payload.orderId)turns a number into a string. -
#input.exchangePropertiesMap.isActive = "true"turns an exchange property holdingtrueorfalseinto a boolean. Avoidboolean()for this, because it returnstruefor every non-empty string,"false"included.
A value comparison such as eq returns an empty result when a field is missing.
A general comparison such as = returns false.
Compare with = in a property that asks for a boolean, because a property that asks for a boolean requires a value.
Property Types
The Utilihive processors ask for these types:
| Type | Processor properties |
|---|---|
Boolean |
|
String |
|
Integer |
|
Any value |
|
Processor-Specific Error Handling
A failing expression fails the message, in either language, except in these processors and properties, which handle a JSONiq result in their own way:
| Processor or property | Handling |
|---|---|
|
|
An empty result or a failing expression means the message is not a fragment, and the processor passes it on. A result of the wrong type fails the message. |
|
A failing expression fails the message with a non-transient data processing error. Dynamic table functions are not supported in JSONiq expressions, and cause a non-transient validation error. |
|
An empty result or a failing expression makes the processor fall back to |
|
|
An empty result, a failing expression, or a result of the wrong type leaves the message without a correlation ID, and the flow processes it as usual. |
Limits
A JSONiq expression has these limits:
-
uh:tableRowLookup()anduh:deleteTableRows()work only in the map processor. An expression that calls one of them fails. -
The
objectPathExprof the payload archiving strategy cannot readprocessorSpec. Reading it fails the expression. -
A top-level updating expression, such as
delete json ..., returns an empty result. If you nest an updating expression inside another expression, the integration flow fails to deploy. -
The dbStatement processor embeds an expression in its statement, between
:##and##, or between:@@and@@. The expression must fit on one line, and it ends at the first closing##or@@. This limit holds in SpEL too.
Cost
A JSONiq expression costs a few microseconds per evaluation. That is several times the cost of a SpEL expression. The difference is small for a property evaluated once per message. It adds up on the properties evaluated many times for one message:
-
groupingKeyExprof the group processor, once per list element -
targetFilterExpressionof the distribute processor, once per target flow -
the
:##parameters of the dbStatement processor, once per parameter
Utilihive Functions
The data mapper comes with the following Utilihive-specific functions. Every function works in the map processor. All but the two dynamic table functions also work in an expression.
| Function | Description | Availability |
|---|---|---|
|
Capitalizes the first character of the given string. |
Map processor and expressions |
|
Converts the first character of the given string to lowercase. |
Map processor and expressions |
|
Deletes rows from a dynamic table. See the Dynamic Tables documentation for more details. |
Map processor only |
|
Throws an |
Map processor and expressions |
|
Maps a given |
Map processor and expressions |
|
Creates a |
Map processor and expressions |
|
Looks up a row from a dynamic table in the Resource Registry. See the Dynamic Tables documentation for more details. |
Map processor only |
|
GIS function that transforms a spatial point between two Coordinate Reference Systems (CRS). [1] |
Map processor and expressions |
|
Generates a random v4 UUID. |
Map processor and expressions |
|
Generates a namespace-based v5 UUID. Must be given |
Map processor and expressions |
SpEL Expressions
A SpEL expression reads the SpEL paths of the standard expression context, such as envelope.payload for the payload.
Dot notation reads a map entry, as in envelope.payload.myEntry.mySubEntry.
List indexes start at 0, as in envelope.payload.myList[0].
Spring converts the result to the type the property asks for.
For SpEL syntax and features, see the Spring Expression Language (SpEL).
By default, SpEL expressions run in safe SpEL evaluation mode in Spring’s SimpleEvaluationContext, with map access enabled and map mutation disabled. For details, see Spring’s documentation on evaluation contexts.
Safe SpEL evaluation allows property, map, and list reads and supported operators.
Safe SpEL evaluation rejects SpEL expressions that:
-
call a method, such as
.toString() -
call a function, such as
#myFunction() -
refer to a type with
T(...) -
call a constructor with
new -
reference a bean with
@beanName -
assign a value with
= -
use increment or decrement operators
++and-- -
reference variables other than
#thisand#root
Flow deployment reports these errors before message processing starts. Review existing SpEL expressions and replace unsupported syntax with property, map, or index reads and supported operators.
If you cannot rewrite an expression to use safe SpEL, migrate it to JSONiq. For more information, see Migrate to JSONiq Expressions.
When the mc.flow-server.featureToggles.allowUnsafeSpelEvaluation feature toggle is enabled, all SpEL expressions run in Spring’s StandardEvaluationContext, which permits broader SpEL capabilities, including method calls and data changes. Expressions containing unsafe input can therefore have security implications. See Spring’s documentation on SpEL evaluation context security.
Keep the toggle set to false unless you must temporarily restore legacy expressions during migration. Consult your SRE before enabling it, and set it to false again when the migration is complete.
mc.flow-server.featureToggles.allowUnsafeSpelEvaluation: false
|
SpEL remains the default expression language for backwards compatibility; it is not the recommended language. Use JSONiq for new expressions and when migrating existing SpEL expressions. |
| The migration guide shows how to migrate SpEL expressions to JSONiq. |
String Constants
An expression can be a string constant.
In JSONiq, the string takes double quotes: """jsoniq:"my hardcoded string" """ in a Kotlin raw string.
In SpEL, it takes single quotes: "'my hardcoded string'" in a Kotlin string.