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:

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

#input.envelope.payload

envelope.payload

Any

The payload of the message. Typically, a JSON compliant object.

#input.envelope.messageId

envelope.messageId

String

The unique identifier of the message exchange. Generated by the server.

#input.envelope.originalMessageId

envelope.originalMessageId

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 split processor.

#input.envelope.stash

envelope.stash

Object

The message exchange stash.

#input.envelope.flowId

envelope.flowId

String

The ID of the flow the message is currently being processed in.

#input.envelope.ownerId

envelope.ownerId

String

The ID of the organization that owns the flow.

#input.envelope.messageExchangeProperties

envelope.messageExchangeProperties

Array of objects

The message’s exchange properties, each an object with a name and a value. Most expressions read exchangePropertiesMap instead.

#input.exchangePropertiesMap

exchangePropertiesMap

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

targetFilterExpression of the distribute processor

targetFlowId

The ID of the target flow being evaluated.

groupingKeyExpr and defaultGroupingKeyExpr of the group processor

element

The list element being grouped.

outputTempFileNameExpr of the writeFiles processor

targetFileName

The name of the file being written.

objectPathExpr of the payload archiving strategy

processorPosition

The position in the flow of the processor whose payload is archived, or source for the source processor.

processorNormalizedName

The name of the processor whose payload is archived, with characters that are unsafe in a path replaced by -.

payloadOrigin

MESSAGE for an archived message payload, or RESPONSE for an archived response payload.

processorSpec

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

JSON compliant

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 parse-json(#input.envelope.payload).orderId.

A byte stream

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.

Binary

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

#input.payload.name

#input.envelope.payload.name

envelope.payload.name

A stash entry

#input.stash.myKey

#input.envelope.stash.myKey

envelope.stash.myKey

An exchange property

#input.messageExchangeProperties[][##.name eq "myProperty"].value

#input.exchangePropertiesMap.myProperty

exchangePropertiesMap.myProperty

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 null

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 4 div 2, and rejects 5 div 2, which is 2.5.

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 holding true or false into a boolean. Avoid boolean() for this, because it returns true for 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

filter

true lets the message proceed, and false filters it out. An empty result fails the message. If the field is missing, eq fails the message and = filters it out.

aggregate

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.

dbStatement

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.

scheduledHandoff

An empty result or a failing expression makes the processor fall back to delayExpr, then defaultDelay. A result of the wrong type fails the message.

correlationIdExpr of the flow

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() and uh:deleteTableRows() work only in the map processor. An expression that calls one of them fails.

  • The objectPathExpr of the payload archiving strategy cannot read processorSpec. 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:

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

uh:capitalized()

Capitalizes the first character of the given string.

Map processor and expressions

uh:decapitalized()

Converts the first character of the given string to lowercase.

Map processor and expressions

uh:deleteTableRows()

Deletes rows from a dynamic table. See the Dynamic Tables documentation for more details.

Map processor only

uh:fail()

Throws an IllegalArgumentException, with the error message being the string given to the function. See the fail function examples.

Map processor and expressions

uh:formattedDateTime()

Maps a given DateTime string from one format to another, converting between time zones as needed. The source, formats, and time zones are set as properties of a config object. See the DateTime function examples.

Map processor and expressions

uh:formattedCurrentDateTime()

Creates a DateTime string of the current time based on the given formatting pattern and (optional) time zone, which is UTC when not given. See the DateTime function examples.

Map processor and expressions

uh:tableRowLookup()

Looks up a row from a dynamic table in the Resource Registry. See the Dynamic Tables documentation for more details.

Map processor only

uh:transformCoordinate()

GIS function that transforms a spatial point between two Coordinate Reference Systems (CRS). [1]

Map processor and expressions

uh:uuid4()

Generates a random v4 UUID.

Map processor and expressions

uh:uuid5()

Generates a namespace-based v5 UUID. Must be given name and namespace arguments: uh:uuid5(name, namespace). The name is any random string. The namespace is either a random UUID string or one of these UUID strings pre-defined in UUID RFC. Four namespaces are pre-defined: NameSpace_DNS, NameSpace_URL, NameSpace_OID, and NameSpace_X500.

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 #this and #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.


1. This service is using the EPSG dataset. The EPSG dataset is under the ownership of International Association of Oil and Gas Producers (IOGP).