Migrate SpEL Expressions to JSONiq
This guide shows how to rewrite SpEL expressions in JSONiq.
Identify Affected Integration Flows
JSONiq is the preferred language for new expressions. SpEL remains the default and is supported.
Safe SpEL evaluation rejects some SpEL expressions, such as those that call a method or refer to a type with T(...).
The Expressions Guide lists what it rejects.
An integration flow with a rejected expression fails to deploy. Rewrite these expressions first.
Allow Unsafe SpEL Evaluation
An operator can set the flow-server property mc.flow-server.featureToggles.allowUnsafeSpelEvaluation to true.
This turns on unsafe SpEL evaluation, which runs rejected expressions too.
The property applies to every integration flow on the flow-server.
It does not affect JSONiq expressions.
Set the property as the Flow Server Configuration Reference describes. Remove it once all rejected expressions are rewritten.
Rewrite a SpEL Expression
To rewrite a SpEL expression:
-
Add the language prefix
jsoniq:. -
Start each path at
#input. For example,envelope.payloadbecomes#input.envelope.payload. -
Return the type the property expects. JSONiq does not convert results.
-
Run the integration flow’s functional tests.
For example, this target filter of the distribute processor is rejected, because it calls contains:
targetFilterExpression("exchangePropertiesMap['documentType'] == 'invoice' and targetFlowId.contains('-invoices-')")
The JSONiq version:
targetFilterExpression("""jsoniq:#input.exchangePropertiesMap.documentType = "invoice" and contains(#input.targetFlowId, "-invoices-")""")
The tables below show SpEL expressions and their JSONiq versions.
Each row covers one shape, such as a map key or a method call on a string.
Rejected expressions are marked Rejected.
The expressions use the standard expression context.
Target filters also use targetFlowId.
The expressions are shown without the surrounding Kotlin string.
In Kotlin, write a JSONiq expression containing double quotes as a raw string, between """ and """, so the quotes need no escaping as \".
Where the expression ends in a double quote, add a space before the closing """:
filter {
id = "filter-ok-responses"
expression = """jsoniq:#input.exchangePropertiesMap.statusCode = "200" """
}
Reading Data
All SpEL expressions in this section are accepted.
| Shape | SpEL, then JSONiq | Notes |
|---|---|---|
A payload field |
|
|
A map key |
|
SpEL’s dotted form, |
A stash entry |
|
|
An exchange property |
|
|
A name that holds a dot |
|
Quote names that hold dots. Without quotes, the result is empty. |
A list element |
|
|
The size of a list |
|
|
Whether a list is empty |
|
|
Operators
All SpEL expressions in this section are accepted.
| Shape | SpEL, then JSONiq | Notes |
|---|---|---|
Equality |
|
|
Not null |
|
|
Null |
|
True for a missing field and for |
|
|
|
|
|
JSONiq uses |
Ternary operator |
|
|
Elvis operator |
|
|
A chain of Elvis operators |
|
The first value that exists wins. Names that hold dots are quoted. |
Safe navigation |
|
A missing field gives an empty result, so JSONiq needs no |
String concatenation |
|
For a missing property, SpEL gives |
|
|
SpEL matches the whole string. JSONiq matches any part, so anchor the pattern with |
Method Calls on Strings
All SpEL expressions in this section are rejected, because they call methods.
| Shape | SpEL, then JSONiq | Notes |
|---|---|---|
|
|
Rejected. |
|
|
Rejected. Here the payload is a string. |
|
|
Rejected. For a name without a dot, SpEL fails and JSONiq returns the whole name. |
|
|
Rejected. |
|
|
Rejected. Arrays start at 1. |
|
|
Rejected. Names that hold dots are quoted. |
|
|
Rejected. |
|
|
Rejected. For a missing field, SpEL fails and |
Method Calls on Collections
All SpEL expressions in this section are rejected, because they call methods.
The .size and .empty properties are accepted and give the same results.
| Shape | SpEL, then JSONiq | Notes |
|---|---|---|
|
|
Rejected. |
|
|
Rejected. |
Type References
All SpEL expressions in this section are rejected, because they use T(...).
The Utilihive functions replace them.
| Shape | SpEL, then JSONiq | Notes |
|---|---|---|
A random UUID |
|
Rejected. |
The current date and time |
|
Rejected. Returns a string. Pass the flow-server’s time zone to match SpEL. Without it, the time is UTC. |
The current date in a path |
|
Rejected. Pass the flow-server’s time zone to match SpEL. Without it, the date is UTC. |
No JSONiq Equivalent
All SpEL expressions in this section are rejected. JSONiq has no equivalent, because a JSONiq expression only returns data. Use a processor instead.
| Shape | SpEL | Instead |
|---|---|---|
A constructor |
|
Rejected. JSONiq returns JSON data, not Java objects. Build objects with the map processor. A small object can be returned directly, as in |
An assignment to a payload field |
|
Rejected. Change the payload with the map processor. |
An assignment to an exchange property |
|
Rejected. Use the setExchangeProperty processor, as shown below. |
|
|
Rejected. Use the setExchangeProperty processor, as shown below. |
This setExchangeProperty processor sets orderStatus to shipped:
setExchangeProperty {
id = "set-order-status"
propertyName = "orderStatus"
source = """jsoniq:"shipped" """
}
Check for Behavior Changes
A valid JSONiq rewrite can still behave differently from the SpEL expression:
-
Paths start at
#input. For example,exchangePropertiesMapbecomes#input.exchangePropertiesMap, as the standard expression context shows. -
A string payload stays a string, even when it holds JSON. Use
parse-json()to read its fields. -
Results are not converted. SpEL turns
'201'into an integer where a property asks for one. JSONiq fails on"201". -
A missing field gives an empty result.
eqthen returns an empty result, and=returnsfalse. -
uh:formattedCurrentDateTime()uses UTC unless given a time zone. SpEL’snow()uses the flow-server’s time zone. -
Dates are strings. SpEL’s
now()returns a date object.