DataWeave Language Guide

DataWeave is a functional language used in Mule applications to perform data transformations. Before you begin to use DataWeave to code your own powerful and complex data transformations, you must understand the basic concepts of programming and the core features of functional programming languages.

DataWeave enables you to take advantage of the benefits of functional programming, including:

  • Pure functions that always produce the same output given the same input, making them easier for you to debug.

  • Immutable variables that do not change their value during the execution, adding stability to your code.

  • Function signatures that provide additional transparency about what affects a function’s execution, because pure functions depend on only their input parameters to produce a result.

  • Functional languages produce code that’s easier to maintain because the code is concise and clear.

  • A call-by-need strategy (also known as lazy evaluation) that improves performance, because expressions are evaluated only when they are needed and their result is stored to avoid evaluating an expression multiple times if the input parameters are the same.

DataWeave Language Basic Concepts

DataWeave incorporates concepts that are common to most programming languages. All DataWeave scripts consist of a header that contains important directives, including the output format of the transformation, and a body that contains the expression to generate a result.

DataWeave supports different data structures, including simple, complex, and composite types. In most of your DataWeave transformations, you use data selectors, data operators, and functions alongside arrays and objects to perform data mappings or transformations.

You can also import prebuilt DataWeave modules to extend the set of functions available to use in your scripts.

Architecture of a DataWeave Script

A DataWeave script consists of a header and a body, separated by three dashes (---). The header contains language directives (import, for example), defines the output format of the transformation, and can also contain variable and function declarations. The body contains the expression that produces the resulting output, usually a data mapping or a transformation.

Here is an example of a basic DataWeave transformation:

%dw 2.0
output application/json
---
payload

Data Types

DataWeave represents data using values, each of which has a data type associated with it. The type system enables you to apply constraints to variables and function parameters. The supported data types include simple types such as String, Boolean, Number, and Date, as well as composite and complex types such as Array, Object, and Any.

For a complete list of all supported data types, see Type System.

Data Selectors

Data selectors enable you to access fields within a data structure so that you can retrieve data from the payload or any other variable created during the DataWeave script execution. Different types of selectors provide you with a variety of options that you can use to extract data from structures.

For example, you can use the single-value selector payload.userName to retrieve the single value userName, where userName represents an actual key in the input message.

See Selectors for a complete list of data selectors and their usage.

Functions

A function is a code block that contains a group of operations that are executed when the function is called in your application. Functions can accept different parameters as input that can then be used by the operations defined in the function. Additionally, every function returns a value after it finishes processing, and this value can then be used as input for a different function.

Each DataWeave script that you write in any component (like a Transform Message component or Set Payload component) works as an independent program, meaning that all function definitions and calls are unique to the script defined in the component.

See Define DataWeave Functions for additional function definition details.

Function Definition vs. Function Call

In DataWeave, you define functions in the header of the script, creating a blueprint for the process that defines the operations that are executed upon the function call.

When you call a function from a DataWeave expression, you are instructing DataWeave to run all the operations defined in that function at that exact point of the script’s execution.

For example, upper is a built-in function defined in the String module. You can call this function to convert a text value to uppercase. Also, you can use a selector to retrieve a value from the payload, and then execute the upper() function call with this value as the input parameter upper(payload.someKey).

DataWeave supports both prefix notation (function (param1, param2)) and infix notation (param1 function param2) for function calls. Note that you can use infix notation only with functions that accept only two parameters.

See also, Function Signatures.

Data Operators

Data operators are symbols that enable you to perform operations on data to produce a result. DataWeave supports operators that are common in most programming languages like mathematical (+, -, /, *), relational (<, >, <=, >=, ==, ~=), and logical (not, !, and, or) operators.

DataWeave also supports the following operators:

  • Append (<<, +), Prepend (>>), and Remove (-), which enable you to manage data in an array.

  • Scope and Flow control operators (do, using, if, else) that enable you to create a scope where new variables and functions can be declared, or to execute an expression based on a condition.

  • Update (update), which enables you to update specific fields of a data structure.

See DataWeave Operators for a complete list of supported operators, their usage, and examples.

Import Function Modules

By default, DataWeave scripts import the dw::Core function module. To use functions from different modules, import the modules to your script by adding the import directive.

See DataWeave Reference for instructions on using the import directive.

Arrays

An array is a data structure that can contain any number of elements of the supported DataWeave types. To declare an array, use brackets ([ and ]) to enclose a comma-separated list of elements, for example: [1,2,3].

Arrays also support the application of conditional logic to define conditional elements ([(value1) if condition]). For example, the expression [(1) if true, (2) if false, (3) if payload != null] defines the array [1,3] (assuming that payload is not null).

Objects

An object is a collection of zero or more Key and value pairs:

  • Key, which is formed by:

    • Name, composed of a String value as the local name and an optional Namespace.

    • Attributes, a collection of zero or more Name:value pairs, where value can be any possible value.

  • Value, comprising any supported type, including another object, a function call, or an array.

To declare an object, use braces ({ and }) to enclose a comma-separated list of Key:value pairs:

{
  keyName @(keyAttributeName :"keyAttributeValue"): "value",
  User @(role:"admin"): upper("max the mule"),
  myArray: [1,2,3]
}

Objects also support the definition of conditional elements:

{
 (userName: name) if !isBlank(name),
 (userRole: role) if !isBlank(role)
}

Functional Programming Principles

The functional programming paradigm enables developers to build applications by using pure function composition and immutable data. Because functional programming is declarative, functions in DataWeave are just expressions that return a value, instead of imperative statements that change program states.

DataWeave follows the core principles of functional programming, including pure functions, first-class functions, high-order functions, function composition, lambda expressions, and immutable data.

Pure Functions

Pure functions in programming are similar to pure functions in mathematics in that, given a certain input, they always return the same output. Also, executing a function has no impact on other parts of a program, other than producing a result. All functions in DataWeave are pure; they are not affected by external values that could change the result of the execution.

First-Class Functions

The pure functions in DataWeave are also “first-class citizens” in that they support operations that are available to any other data type, including being stored in variables, passed as parameters, and returned as results.

High-Order Functions

High-order functions in programming also have the same concept as in mathematics. These functions take one or more functions as parameters and return a function as a result. The result of a high-order function can then be used as an input parameter in another function, enabling the concept of function composition.

Function Composition

Function composition is the process by which functions are combined to build complex operations. In this case, the result of executing one function serves as an input parameter for the next function. In DataWeave, all data transformations are a chain of expressions, functions composed to obtain a final result that outputs the data as needed.

Example of Function Composition

The following example uses the function pluck to map an object into an array. Then the resulting array serves as input for the function filter, which selects the elements in the array that have a Role equal to ADMIN. During the mapping process, the function upper transforms the role value:

%dw 2.0
output application/json
var collaborators =
{
    Max: {role: "admin", id: "01"},
    Einstein: {role: "dev", id: "02"},
    Astro: {role: "admin", id: "03"},
    Blaze: {role: "user", id: "04"}
}
---
collaborators pluck ((value,key,index)->  {
        "Name": key,
        "Role": upper(value.role),
        "ID": value.id
    })
    filter ((item, index) -> item.Role == "ADMIN")

The following example shows the same expression, but it uses prefix notation. This syntax helps visualize how filter takes as the first parameter the result of pluck.

%dw 2.0
output application/json
var collaborators =
{
    Max: {role: "admin", id: "01"},
    Einstein: {role: "dev", id: "02"},
    Astro: {role: "admin", id: "03"},
    Blaze: {role: "user", id: "04"}
}
---
filter(
   pluck(
       collaborators, (value,key,index)->
       {
           "Name": key,
           "Role": upper(value.role),
           "ID": value.id
       }),
   (item, index) -> item.Role == "ADMIN")

The output of both expressions is:

[
  {
    "Name": "Max",
    "Role": "ADMIN",
    "ID": "01"
  },
  {
    "Name": "Astro",
    "Role": "ADMIN",
    "ID": "03"
  }
]

Lambda Expressions

Lambda expressions, also known as anonymous functions or function literals, are function definitions that do not have an identifier name and can be passed as parameters to high-order functions. Lambda syntax is expressed in the form (a,b) → c, which can be read as “Given a certain input (a and b), execute the following (c).”

For example, the map function in DataWeave accepts a lambda expression as one of its parameters: map(Array<T>, (item: T, index: Number) → R): Array<R>. map iterates over the items in an array and executes the lambda expression that is passed as a parameter. This lambda expression consists of two parameters that represent the value (item) and index (index) of each array element during the execution, and a function definition to execute during the iteration R.

The following code example shows a map function that receives a lambda expression to concatenate the index and the value of each element in the array it iterates on, and then returns these values as a new array:

Source

%dw 2.0
output application/json
---
["Max", "the", "Mule"] map (item, index) -> index ++ " - " ++ item

Output

[
    "0 - Max",
    "1 - the",
    "2 - Mule"
]

See Work with Functions and Lambdas in DataWeave for additional information about Lambda expressions.

Immutable Data

In DataWeave, variables cannot change their assigned value after they are defined. This ensures that there are no unexpected results from executing functions. If you want to store a new value that is the result of an operation, you must declare a new data structure.

Differences with Imperative Programming

If you have a development background in working with object-oriented programming languages, it is important to understand that functional programming separates data from functions and does not use loop control statements.

Separation of Data and Functions

DataWeave does not use assignment statements, which ensures that data remains immutable. Because there are no getter and setter methods, nor classes containing data structures and behavior, each function works only with the data you provide as input. If you need to modify any existing data structure, you create a new copy and apply functions to modify its values.

Control Flow Statements

There are no loop control statements like for or while in DataWeave. In DataWeave, instead of defining how to execute a procedure using iteration, you focus on your current requirements when you write the expressions.

For example, to make changes to a list, you use the map function to apply changes to the elements in the collection. Additionally, you can use the filter function to remove specific elements from a list, or the reduce function to collapse all values from an array into one single value.

However, you can use if-else statements to apply a condition when executing an expression:

%dw 2.0
var myVar = { country : "FRANCE" }
output application/json
---
if (myVar.country == "USA")
  { currency: "USD" }
else { currency: "EUR" }

DataWeave Examples

For usage examples of map, reduce, and filter, see:

Was this article helpful?

💙 Thanks for your feedback!

Edit on GitHub