inputExpression match {
case <condition> -> <routing expression>
case <condition> -> <routing expression>
else -> <default routing expression>
}
Pattern Matching in DataWeave Through match Statements
The match
statement behaves like a match
or switch
statement in other
languages, like Java or C++, and routes an input expression to a particular
output expression based on some conditions.
Before you begin, note that 2.x versions of DataWeave are used by Mule 4 apps. For
DataWeave in Mule 3 apps, refer to the
DataWeave version 1.2 documentation.
For other Mule versions, you can use the version selector in the DataWeave table of contents.
As in other languages, the DataWeave match
statement provides a compact way to
organize multiple, chained if-else
statements. A match
expression consists
of a list of case
statements that optionally end with an else
statement.
Each case
statement consists of a conditional selector expression that must
evaluate to true
or false
. This conditional expression corresponds to the
condition expressed by an if
statement, followed by a corresponding DataWeave
routing expression that can include the match
statement’s input expression.
Each case
statement can be an if
statement, or the case
statement can use
other pattern-matching shortcuts to define the case
statement’s condition. If
a case
statement is false
, its routing expression is ignored.
DataWeave supports four different types of patterns for a case
statement’s
condition:
Each case can be named or unnamed. If the case is named, the name stores the input expression as a local variable that can be used both in that case statement’s conditional expression and in the corresponding routing expression.
value match {
case (<name>:) <condition> -> <routing expression>
case (<name>:) <condition> -> <routing expression>
else -> <when none of them matched>
}
As this example shows, the expression returns the results of the first matching
case
statement:
%dw 2.0
output application/json
---
"hello world" match {
case word matches /(hello)\s\w+/ -> word[1] as String ++ " was matched"
case literalMatch: "hello world" -> upper(literalMatch)
case hasOne if( hasOne is Object and hasOne.three? ) -> hasOne.three
else -> $
}
"hello was matched"
Notice that you can refer to the input expression ("hello world") through each
case
statement’s local variable name (that is, word
or literalMatch
),
while the else
statement can refer to the input expression using the default
parameter name $
, an unnamed parameter.
To name the input expression in the else
statement, you can
replace the else
statement with a case
statement that is always true
:
%dw 2.0
output application/json
---
"hello world" match {
case word matches /(hello)\s\w+/ -> word[1] ++ " was matched"
case literalMatch: "hello world" -> upper(literalMatch)
case last if(true) -> last
}
For use cases in which the input is of type String, you can also use the match function in the dw::Core
module to test the string against a regex pattern. The function returns an array with the strings that match the entire regex and any capture groups.
For use cases that require a Boolean result based on whether a string matches a regex pattern, you can use the matches function in the dw::Core
module instead of using pattern matching or the module’s match
function.
Pattern Matching to Literal Values
Matches when the evaluated value equals a simple literal value.
In this example, the first field matches the value in myInput.string
and
returns a boolean. The second field performs the same match, but it returns an
object that contains both a boolean and a reference to the validated value.
%dw 2.0
var myInput = {
"string": "Emiliano"
}
output application/json
---
{
a: myInput.string match {
case "Emiliano" -> true
case "Mariano" -> false
},
b: myInput.string match {
case str: "Emiliano" -> { "matches": true, value: str }
case str: "Mariano" -> { "matches": false, value: str }
}
}
{
"a": true,
"b": {
"matches": true,
"value": "Emiliano"
}
}
Pattern Matching on Expressions
Matches when a given expression returns true
.
In this example, the first field matches the value of myInput.string
against
two alternatives and conditionally appends a different string to it. The second
field evaluates if the value in myInput.number
is greater than (>
),
less than (<
), or equal to (==
) 3 and returns the corresponding string.
%dw 2.0
var myInput = {
"string": "Emiliano",
"number": 3.14
}
output application/json
---
{
a: myInput.string match {
case str if str == "Mariano" -> str ++ " de Achaval"
case str if str == "Emiliano" -> str ++ " Lesende"
},
b: myInput.number match {
case num if num == 3 -> "equal"
case num if num > 3 -> "greater than"
case num if num < 3 -> "less than"
}
}
{
"a": "Emiliano Lesende",
"b": "greater than"
}
Pattern Matching on the Data Type
Matches when the evaluated value is the specified data type.
In this example, the first field evaluates the data type of myInput.a
and returns
a string with the corresponding type name. The second field is similar but also
returns the value of the myInput.b
.
%dw 2.0
var myInput =
{
"a": "Emiliano",
"b": 3.14
}
output application/json
---
{
a: myInput.a match {
case is Object -> 'OBJECT'
case is String -> 'STRING'
case is Number -> 'NUMBER'
case is Boolean -> 'BOOLEAN'
case is Array -> 'ARRAY'
case is Null -> 'NULL'
else -> 'ANOTHER TYPE'
},
b: myInput.b match {
case y is Object -> { 'Type': { 'OBJECT' : y} }
case y is String -> { 'Type': { 'STRING' : y} }
case y is Number -> { 'Type': { 'NUMBER' : y} }
case y is Boolean -> { 'Type': { 'BOOLEAN' : y} }
case y is Array -> { 'Type': { 'ARRAY' : y} }
case y is Null -> { 'Type': { 'NULL' : y} }
else -> { 'Type': { 'ANOTHER TYPE' : myInput.b} }
}
}
{
"a": "STRING",
"b": {
"Type": {
"NUMBER": 3.14
}
}
}
Pattern Matching on Regular Expressions
Matches when the evaluated value fits a given regular expression (regex),
specifically a regex "flavor" supported by Java. In this example, the input
variable (myInput
) includes an array of strings. The script uses the map
function
to iterate through the array. It evaluates each element against a Java regex
and outputs an object based on matches to the input.
%dw 2.0
var myInput = {
"phones": [
"+1 (415) 229-2009",
"(647) 456-7008"
]
}
output application/json
---
{
a: myInput.phones map ($ match {
case phone matches /\+(\d+)\s\((\d+)\)\s(\d+\-\d+)/ -> { country: phone[1]}
case phone matches /\((\d+)\)\s(\d+\-\d+)/ -> { area: phone[1], number: phone[2] }
}),
b: myInput.phones map ($ match {
case phone matches /\+(\d+)\s\((\d+)\)\s(\d+\-\d+)/ -> { country: phone[1], area: phone[2], number: phone[3] }
case phone matches /\((\d+)\)\s(\d+\-\d+)/ -> { area: phone[1], number: phone[2] }
})
}
{
"a": [
{
"country": "1"
},
{
"area": "647",
"number": "456-7008"
}
],
"b": [
{
"country": "1",
"area": "415",
"number": "229-2009"
},
{
"area": "647",
"number": "456-7008"
}
]
}
Deconstructors on Collections in DataWeave
DataWeave supports two types of collections: Arrays and Objects. Arrays are a sequence of values, while Objects are a sequence of key-value pairs.
Deconstruction on Arrays
An Array can be seen as a head (the first element of the Array) and a tail (a new Array with the reminder of the elements). For example, in the array [1,2,3,4]
, 1
is the head and 2,3,4
is the tail.
To match over an array, use the following syntax, where the script gets the first element of an array or returns null if the array is empty:
[1,2,3,4] match {
case [head ~ tail] -> head
case [] -> null
}
This kind of deconstruction is useful when used with recursive functions.
Example: Build a joinBy
Function
This example builds a joinBy
function that takes an array of string and returns a string with all the elements of the Array concatenated with the given separator:
fun joinBy(elements: Array<String>, separator: String): String =
elements match {
case [head ~ tail] -> head ++ separator ++ joinBy(tail, separator) //When head and tail append and recursively
case [] -> "" //When the array is empty return empty array
}
Example: Build a Filter Function
This example builds a higher-order filter function:
fun filter<T>(elements: Array<T>, predicate: (item:T) -> Boolean): Array<T> =
elements match {
case [head ~ tail] ->
if(predicate(head))
[head] ++ filter(tail, predicate)
else
filter(tail, predicate)
case [] -> []
}
Deconstruction on Objects
Objects in DataWeave are similar to Arrays but each element consists of a key and a value. An Object can also be seen as a head (the first key-value pair) and a tail (a new Object with the rest of the key-value pairs).
To match over an object, use the following syntax, where the script gets the first value of an object, which in this case is "Word":
{hello: "world", test: true} match {
case {headKey: headValue ~ tail} -> headValue
case {} -> null
}