8.3 Custom Functions, Format Transformations & Type Coercion
Key Takeaways
- Custom functions are declared in the DataWeave header using the fun keyword with optional typed parameters, default values, and return type declarations (e.g., fun calculateTax(amount: Number, rate: Number = 0.08): Number = amount * rate).
- Standard DataWeave utility modules (dw::core::Strings, dw::core::Arrays, dw::core::Objects, dw::util::Values) provide enterprise manipulation functions imported via header directives (import * from dw::core::Strings).
- String manipulation functions like scan, match, matches, replaceAll, splitBy, and words enable pattern matching, regular expression extraction, and text parsing directly inside transformations.
- DataWeave handles multi-format transformations seamlessly between JSON, XML, CSV, FlatFile, and Java using output directives (output application/json, output application/xml, output application/csv, output application/java).
- Reader and writer properties configure MIME type behavior such as CSV headers (header=true, separator=','), XML namespaces and single root element requirements, JSON indentation (indent=false), and null stripping (skipNullOn='everywhere').
Custom Functions, Format Transformations & Type Coercion
Beyond basic expressions and collection operators, production Mule 4 applications require reusable transformation logic, robust string manipulation, regex pattern extraction, and seamless format translation between heterogeneous systems (such as translating incoming CSV files from FTP servers into XML payloads for legacy SOAP backends or JSON for modern REST APIs).
1. Declaring Custom Functions & Lambdas
Custom functions encapsulate reusable business logic directly within a DataWeave script header using the fun keyword.
+-----------------------------------------------------------------------------------------+
| CUSTOM FUNCTION DECLARATION |
| |
| Header Declaration: |
| fun calculateDiscount(price: Number, discountRate: Number = 0.10): Number = |
| price * (1 - discountRate) |
| |
| Components of Function: |
| 1. fun --> Keyword initiating function declaration |
| 2. calculateDiscount --> Function name identifier |
| 3. (price: Number...) --> Parameter list with optional type annotations & defaults |
| 4. : Number --> Optional explicit return type annotation |
| 5. = --> Assignment operator binding signature to expression body |
| 6. price * (1 - ...) --> Pure functional expression body |
+-----------------------------------------------------------------------------------------+
Function Features in DataWeave:
- Type Annotations: Parameters and return types can declare explicit DataWeave types (
Number,String,Array<Object>, etc.) for compile-time validation. - Default Parameter Values: Arguments can specify fallback defaults (e.g.,
rate: Number = 0.0825). When the caller omits that parameter, the default is automatically applied. - Pure & Referential Transparency: DataWeave functions are pure; given the same inputs, they always return the same output without modifying external state.
- Recursive Functions: Functions can invoke themselves recursively (supporting tail-call optimization) to traverse tree structures or generate iterative sequences.
Custom Function Script Example:
%dw 2.0
output application/json
// Custom function with default tax rate
fun calculateTotal(subtotal: Number, taxRate: Number = 0.08): Number =
subtotal * (1 + taxRate)
// Custom function with pattern matching / ternary logic
fun maskCreditCard(cardNumber: String): String =
if (sizeOf(cardNumber) >= 16)
"****-****-****-" ++ cardNumber[-4 to -1]
else
"INVALID_CARD"
---
{
customer: payload.customerName,
maskedCard: maskCreditCard(payload.accountNumber),
standardTotal: calculateTotal(payload.subtotal),
customTaxTotal: calculateTotal(payload.subtotal, 0.12)
}
2. The DataWeave Standard Library (dw::*)
DataWeave bundles a comprehensive standard library organized under the dw namespace. Modules must be imported in the script header before their functions can be called.
+-----------------------------------------------------------------------------------------+
| DATAWEAVE STANDARD MODULES |
| |
| +---------------------------------------------------------------------------------+ |
| | dw::core::Strings --> capitalize, camelize, dasherize, words, scan, match, | |
| | replaceAll, substringBefore, substringAfter, wrapWith | |
| +---------------------------------------------------------------------------------+ |
| +---------------------------------------------------------------------------------+ |
| | dw::core::Arrays --> countBy, divide, drop, take, slice, some, every, | |
| | partition, splitAt, join | |
| +---------------------------------------------------------------------------------+ |
| +---------------------------------------------------------------------------------+ |
| | dw::core::Objects --> mergeWith, divide, keySet, valueSet, entryList | |
| +---------------------------------------------------------------------------------+ |
| +---------------------------------------------------------------------------------+ |
| | dw::util::Values --> update, mask, field | |
| +---------------------------------------------------------------------------------+ |
| +---------------------------------------------------------------------------------+ |
| | dw::Crypto --> HMACBinary, HMACJVM, MD5, SHA1 | |
| +---------------------------------------------------------------------------------+ |
+-----------------------------------------------------------------------------------------+
Import Syntax Variants
%dw 2.0
output application/json
// 1. Wildcard Import: imports all functions into current scope
import * from dw::core::Strings
// 2. Targeted Import: imports only specific named functions
import countBy, divide from dw::core::Arrays
// 3. Module Alias: namespaced invocation
import dw::util::Values
---
{
capitalized: capitalize("mulesoft integration"), // -> "Mulesoft integration"
camelized: camelize("customer_account_id"), // -> "customerAccountId"
arrayBatches: divide([1, 2, 3, 4, 5, 6], 2), // -> [[1,2], [3,4], [5,6]]
updatedPayload: Values::update(payload, ["status"], (old) -> "PROCESSED")
}
3. String Manipulation & Regular Expressions
Transforming text patterns is central to data integration. The table below outlines core string functions and regular expression operations:
| Function | Module | Description & Example |
|---|---|---|
capitalize | dw::core::Strings | Capitalizes the first character of a string: capitalize("hello world") -> "Hello world". |
camelize | dw::core::Strings | Converts underscore/hyphen text to camelCase: camelize("order_id") -> "orderId". |
words | dw::core::Strings | Splits a sentence into an array of words: words("Mule 4 Runtime") -> ["Mule", "4", "Runtime"]. |
replaceAll | dw::core::Strings | Replaces occurrences matching string or regex: replaceAll("123-456", "-", "") -> "123456". |
scan | dw::core::Strings | Returns an array of all regex matches with capture groups: scan("user@test.com", /([a-z]+)@([a-z]+)\.com/). |
match | dw::core::Strings | Returns capture groups if the string matches the regex pattern, else empty array. |
matches | dw::core::Strings | Returns boolean true if string matches regex pattern, else false. |
splitBy | Built-in Core | Splits a string by delimiter: "A,B,C" splitBy "," -> ["A", "B", "C"]. |
joinBy | Built-in Core | Joins an array of strings with delimiter: ["A", "B", "C"] joinBy "-" -> "A-B-C". |
Regex Pattern Matching Example:
%dw 2.0
output application/json
import * from dw::core::Strings
var emailStr = "support@enterprise.com, billing@enterprise.org"
---
{
// scan returns array of match arrays with capture groups
extractedEmails: scan(emailStr, /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/)
}
4. Multi-Format Transformations: JSON, XML, CSV & Java
DataWeave excels at translating data representations across completely different serialization protocols with zero boilerplate parser code.
+-----------------------------------------------------------------------------------------+
| CROSS-FORMAT TRANSLATION |
| |
| [CSV Input: Accounts.csv] [JSON Input: REST Request] |
| id,name,tier { "id": 101, "name": "Acme" } |
| \ / |
| \ / |
| v v |
| +-----------------------------------------------------------------------------+ |
| | DATAWEAVE TRANSFORMATION | |
| | %dw 2.0 | |
| | output application/xml | |
| +-----------------------------------------------------------------------------+ |
| | |
| v |
| [XML Output: SOAP / Enterprise] |
| <accounts> |
| <account id="101"><name>Acme</name></account> |
| </accounts> |
+-----------------------------------------------------------------------------------------+
1. JSON (output application/json)
- Default format for REST APIs.
- Writer properties include
indent=false(compact one-line output) andskipNullOn="everywhere"(strips null fields globally).
2. XML (output application/xml)
- Strict Single Root Element Rule: Every valid XML document must have exactly one root element.
- Generating XML attributes uses the
@(attributeName: value)syntax.
// Input Array:
// [ { "id": "A1", "name": "Router" }, { "id": "A2", "name": "Switch" } ]
%dw 2.0
output application/xml
---
{
inventory @(location: "US-East", count: sizeOf(payload)): {
(payload map ((item) -> {
device @(deviceId: item.id): {
modelName: item.name
}
}))
}
}
Output XML:
<?xml version='1.0' encoding='UTF-8'?>
<inventory location="US-East" count="2">
<device deviceId="A1">
<modelName>Router</modelName>
</device>
<device deviceId="A2">
<modelName>Switch</modelName>
</device>
</inventory>
3. CSV (output application/csv)
- CSV structures represent tabular data and must evaluate to an
Array<Object>. - Writer properties control header row inclusion (
header=true|false), field delimiter (separator=","orseparator=";"), and quotation rules (quoteValues=true).
%dw 2.0
output application/csv header=true, separator=",", quoteValues=true
---
payload map ((user) -> {
"User ID": user.id,
"Full Name": user.firstName ++ " " ++ user.lastName,
"Email": user.emailAddress
})
4. Java (output application/java)
- Produces native in-memory Java collections (
java.util.HashMap,java.util.ArrayList, or specified POJOs) for consumption by Java connectors, custom Java components, or legacy SDKs.
5. Reader and Writer Properties Reference
Reader properties configure how the runtime parses incoming streams, while writer properties govern how the runtime serializes output payloads.
Common Writer Properties Table:
| MIME Type | Property | Default | Description |
|---|---|---|---|
application/json | indent | true | When set to false, outputs compact single-line JSON without whitespace. |
application/json | skipNullOn | "none" | When set to "everywhere", automatically deletes all keys containing null values at all nesting levels. |
application/xml | indent | true | Enables or disables XML line indentation and pretty-printing. |
application/xml | inlineCloseOn | "none" | Closes empty XML tags inline (e.g., <item/> instead of <item></item>) when set to "empty". |
application/csv | header | true | Boolean indicating whether to write column header names in the first row. |
application/csv | separator | "," | Character delimiter separating CSV columns (e.g., separator="\t" for TSV). |
application/csv | quoteValues | false | When true, encloses every field in double quotes ("val"). |
Reader Properties Configuration Example:
When reading non-standard CSV files (such as pipe-delimited files without headers), configure the mimeType on the inbound Listener or File Read component:
<file:read
path="orders.dat"
outputMimeType="application/csv; separator='|'; header=false"
doc:name="Read Pipe-Delimited File" />
6. Exam Watch: Core Function & Transformation Scenarios
[!IMPORTANT] XML Requires Exactly One Root Object An expression like
%dw 2.0 output application/xml --- payload map { item: $ }will fail withTrying to output second rootbecause an Array was returned at the root level. You must enclose the map inside a single parent object{ rootElement: { (payload map ...) } }.
[!WARNING] CSV Payloads Must Be Arrays of Objects DataWeave cannot serialize a scalar value, a nested object, or an array of simple strings (
["A", "B"]) directly to CSV. The root expression must evaluate to anArray<Object>where each object represents a row.
[!TIP] Automated Null Stripping with
skipNullOnInstead of writing verbosefilterObjectlogic across multiple nested objects to remove null attributes, addoutput application/json skipNullOn="everywhere"to the DataWeave script header.
A developer writes a DataWeave transformation with output application/xml and the body expression: payload map ((item) -> { "order": { "id": item.id, "total": item.amount } }). The input payload is an array of 5 order objects. Why does this transformation fail at runtime?
A developer needs to import the capitalize and words functions from the standard DataWeave Strings module. Which header directive correctly imports these specific functions so they can be invoked directly by name in the script body?
A developer is generating a CSV payload from an Array of customer objects and must omit the header row and enclose all column values in quotation marks. How should the DataWeave output directive in the header be configured?
A custom function is declared in a DataWeave header as: fun calculateDiscount(price: Number, rate: Number = 0.10): Number = price * rate. What is the resulting output when the function is invoked in the body as calculateDiscount(200)?