DataWeave este limbajul de transformare al MuleSoft. Acest ghid acopera totul, de la transformari de baza pana la tehnici avansate care rezolva provocari reale de integrare.
Concepte fundamentale DataWeave
Sa intelegem conceptele de baza ale DataWeave:
%dw 2.0
output application/json
// Basic transformation structure
{
// Header section defines output format
// Body section contains transformation logic
}Tipuri de date si conversii
%dw 2.0
output application/json
var inputData = {
"stringNum": "123",
"dateString": "2025-01-15",
"boolString": "true",
"nullValue": null
}
---
{
// Type coercion examples
asNumber: inputData.stringNum as Number,
asDate: inputData.dateString as Date {format: "yyyy-MM-dd"},
asBoolean: inputData.boolString as Boolean,
// Default values for null handling
withDefault: inputData.nullValue default "N/A",
// Conditional coercion
safeNumber: if (inputData.stringNum is String)
inputData.stringNum as Number
else
0
}Transformari JSON catre XML
Converteste payload-uri JSON in XML cu namespace-uri:
%dw 2.0
output application/xml
ns soap http://schemas.xmlsoap.org/soap/envelope/
ns ord http://example.com/orders
var orderJson = {
"orderId": "ORD-12345",
"customer": {
"id": "CUST-001",
"name": "Acme Corp",
"email": "orders@acme.com"
},
"items": [
{"sku": "PROD-A", "quantity": 10, "price": 25.99},
{"sku": "PROD-B", "quantity": 5, "price": 49.99}
],
"total": 509.85
}
---
{
soap#Envelope: {
soap#Header: {},
soap#Body: {
ord#CreateOrder: {
ord#OrderHeader: {
ord#OrderId: orderJson.orderId,
ord#OrderDate: now() as String {format: "yyyy-MM-dd'T'HH:mm:ss"},
ord#Customer: {
ord#CustomerId: orderJson.customer.id,
ord#CustomerName: orderJson.customer.name,
ord#Email: orderJson.customer.email
}
},
ord#OrderLines: {
(orderJson.items map (item, index) -> {
ord#OrderLine: {
ord#LineNumber: index + 1,
ord#SKU: item.sku,
ord#Quantity: item.quantity,
ord#UnitPrice: item.price,
ord#LineTotal: item.quantity * item.price
}
})
},
ord#OrderTotal: orderJson.total
}
}
}
}Transformari XML catre JSON
Gestioneaza XML complex cu atribute si namespace-uri:
%dw 2.0
output application/json
var xmlInput = read('<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetCustomerResponse xmlns="http://example.com/customer">
<Customer id="CUST-001" status="active">
<Name>Acme Corporation</Name>
<Addresses>
<Address type="billing" primary="true">
<Street>123 Main St</Street>
<City>New York</City>
<Country>USA</Country>
</Address>
<Address type="shipping">
<Street>456 Warehouse Blvd</Street>
<City>Newark</City>
<Country>USA</Country>
</Address>
</Addresses>
<Contacts>
<Contact role="primary">John Doe</Contact>
<Contact role="billing">Jane Smith</Contact>
</Contacts>
</Customer>
</GetCustomerResponse>
</soap:Body>
</soap:Envelope>', "application/xml")
---
{
customer: {
// Access attributes with @
id: xmlInput.Envelope.Body.GetCustomerResponse.Customer.@id,
status: xmlInput.Envelope.Body.GetCustomerResponse.Customer.@status,
name: xmlInput.Envelope.Body.GetCustomerResponse.Customer.Name,
// Transform addresses array
addresses: xmlInput.Envelope.Body.GetCustomerResponse.Customer.Addresses.*Address map (addr) -> {
addressType: addr.@type,
isPrimary: addr.@primary default "false" == "true",
street: addr.Street,
city: addr.City,
country: addr.Country
},
// Transform contacts with role mapping
contacts: xmlInput.Envelope.Body.GetCustomerResponse.Customer.Contacts.*Contact map (contact) -> {
role: contact.@role,
name: contact
}
}
}Procesarea fisierelor CSV si flat file
Gestioneaza CSV cu delimitatori si headere personalizate:
%dw 2.0
output application/json
var csvData = "employee_id|first_name|last_name|department|salary|hire_date
E001|John|Doe|Engineering|85000|2020-03-15
E002|Jane|Smith|Marketing|72000|2019-08-22
E003|Bob|Johnson|Engineering|92000|2018-01-10
E004|Alice|Williams|Sales|78000|2021-05-01"
var parsedCsv = read(csvData, "application/csv", {
separator: "|",
header: true
})
---
{
totalEmployees: sizeOf(parsedCsv),
// Group by department
byDepartment: parsedCsv groupBy $.department mapObject ((employees, dept) -> {
(dept): {
count: sizeOf(employees),
totalSalary: sum(employees.salary map ($ as Number)),
avgSalary: avg(employees.salary map ($ as Number)),
employees: employees map {
id: $.employee_id,
name: $.first_name ++ " " ++ $.last_name,
salary: $.salary as Number
}
}
}),
// Find highest paid
highestPaid: (parsedCsv orderBy -(($.salary) as Number))[0] then {
name: $.first_name ++ " " ++ $.last_name,
salary: $.salary as Number,
department: $.department
}
}Generare output CSV
%dw 2.0
output application/csv separator=",", header=true, quoteValues=true
var jsonOrders = [
{orderId: "O001", customer: "Acme", product: "Widget A", qty: 10, price: 25.99},
{orderId: "O002", customer: "Beta Inc", product: "Widget B", qty: 5, price: 49.99},
{orderId: "O003", customer: "Acme", product: "Widget C", qty: 20, price: 15.50}
]
---
jsonOrders map {
"Order ID": $.orderId,
"Customer Name": $.customer,
"Product": $.product,
"Quantity": $.qty,
"Unit Price": $.price,
"Line Total": $.qty * $.price,
"Order Date": now() as String {format: "yyyy-MM-dd"}
}Operatii avansate pe array-uri
Stapaneste manipularile complexe de array-uri:
%dw 2.0
output application/json
var salesData = [
{region: "North", product: "A", q1: 100, q2: 150, q3: 120, q4: 180},
{region: "North", product: "B", q1: 80, q2: 90, q3: 110, q4: 95},
{region: "South", product: "A", q1: 200, q2: 180, q3: 220, q4: 250},
{region: "South", product: "B", q1: 150, q2: 160, q3: 140, q4: 170},
{region: "East", product: "A", q1: 90, q2: 100, q3: 95, q4: 110},
{region: "West", product: "A", q1: 120, q2: 130, q3: 125, q4: 140}
]
// Helper function for annual total
fun annualTotal(record) = record.q1 + record.q2 + record.q3 + record.q4
---
{
// Flatten quarterly data to rows
flattenedData: salesData flatMap (record) -> [
{region: record.region, product: record.product, quarter: "Q1", sales: record.q1},
{region: record.region, product: record.product, quarter: "Q2", sales: record.q2},
{region: record.region, product: record.product, quarter: "Q3", sales: record.q3},
{region: record.region, product: record.product, quarter: "Q4", sales: record.q4}
],
// Pivot: Region totals by quarter
regionQuarterPivot: salesData groupBy $.region mapObject ((records, region) -> {
(region): {
Q1: sum(records.q1),
Q2: sum(records.q2),
Q3: sum(records.q3),
Q4: sum(records.q4),
Annual: sum(records map annualTotal($))
}
}),
// Product performance ranking
productRanking: (salesData groupBy $.product mapObject ((records, product) -> {
(product): sum(records map annualTotal($))
}) pluck ((value, key) -> {product: key, totalSales: value}))
orderBy -$.totalSales,
// Find best quarter per region
bestQuarterByRegion: salesData groupBy $.region mapObject ((records, region) -> {
(region): do {
var totals = {
Q1: sum(records.q1),
Q2: sum(records.q2),
Q3: sum(records.q3),
Q4: sum(records.q4)
}
var maxVal = max(totals pluck $)
---
(totals filterObject ((v, k) -> v == maxVal) pluck $$)[0]
}
}),
// Distinct regions and products
distinctRegions: salesData.region distinctBy $,
distinctProducts: salesData.product distinctBy $,
// Cross join for all combinations
allCombinations: (salesData.region distinctBy $) flatMap ((region) ->
(salesData.product distinctBy $) map ((product) -> {
region: region,
product: product
})
)
}Gestionarea cheilor dinamice
Lucreaza cu chei dinamice si variabile:
%dw 2.0
output application/json
var dynamicData = {
"field_001": "value1",
"field_002": "value2",
"custom_abc": "valueA",
"custom_xyz": "valueB",
"metadata_created": "2025-01-15",
"metadata_updated": "2025-01-20"
}
var keyMapping = {
"field_001": "primaryField",
"field_002": "secondaryField"
}
---
{
// Rename keys dynamically
renamedFields: dynamicData mapObject ((value, key) -> {
((keyMapping[key as String] default key)): value
}),
// Group by key prefix
groupedByPrefix: dynamicData
pluck ((value, key) -> {key: key, value: value})
groupBy ((item) -> (item.key as String) splitBy "_")[0]
mapObject ((items, prefix) -> {
(prefix): items reduce ((item, acc = {}) -> acc ++ {
(((item.key as String) splitBy "_")[1 to -1] joinBy "_"): item.value
})
}),
// Filter keys by pattern
customFieldsOnly: dynamicData filterObject ((v, k) ->
(k as String) startsWith "custom_"
),
// Dynamic key construction
constructedObject: ["alpha", "beta", "gamma"] reduce ((item, acc = {}) ->
acc ++ {("dynamic_" ++ item): upper(item)}
)
}Gestionarea erorilor si validarea datelor
Pattern-uri robuste de gestionare a erorilor:
%dw 2.0
output application/json
var inputPayload = {
"orders": [
{"id": "O1", "amount": "100.50", "currency": "USD", "date": "2025-01-15"},
{"id": "O2", "amount": "invalid", "currency": "EUR", "date": "2025-01-16"},
{"id": "O3", "amount": "200.00", "currency": null, "date": "bad-date"},
{"id": null, "amount": "150.00", "currency": "GBP", "date": "2025-01-17"}
]
}
// Validation function
fun validateOrder(order) = {
isValid: (order.id != null) and
(order.amount is String and (order.amount matches /^\d+(\.\d{2})?$/)) and
(order.currency != null) and
(order.date is String and (order.date matches /^\d{4}-\d{2}-\d{2}$/)),
errors: (
(if (order.id == null) ["Missing order ID"] else []) ++
(if (!(order.amount is String and (order.amount matches /^\d+(\.\d{2})?$/))) ["Invalid amount format"] else []) ++
(if (order.currency == null) ["Missing currency"] else []) ++
(if (!(order.date is String and (order.date matches /^\d{4}-\d{2}-\d{2}$/))) ["Invalid date format"] else [])
)
}
// Safe type conversion
fun safeToNumber(val) =
if (val is String and (val matches /^-?\d+(\.\d+)?$/))
val as Number
else
null
fun safeToDate(val, format) =
try (() -> val as Date {format: format})
orElse null
---
{
// Validate all orders
validationResults: inputPayload.orders map ((order, index) -> {
orderIndex: index,
order: order,
validation: validateOrder(order)
}),
// Separate valid and invalid
validOrders: inputPayload.orders filter ((order) -> validateOrder(order).isValid),
invalidOrders: inputPayload.orders
map ((order, index) -> {order: order, index: index, validation: validateOrder(order)})
filter ((item) -> !item.validation.isValid)
map {
orderIndex: $.index,
originalData: $.order,
errors: $.validation.errors
},
// Transform with safe conversions
processedOrders: inputPayload.orders map ((order) -> {
id: order.id default "UNKNOWN",
amount: safeToNumber(order.amount) default 0,
currency: order.currency default "USD",
date: safeToDate(order.date, "yyyy-MM-dd"),
processed: true
}),
// Summary
summary: {
total: sizeOf(inputPayload.orders),
valid: sizeOf(inputPayload.orders filter ((o) -> validateOrder(o).isValid)),
invalid: sizeOf(inputPayload.orders filter ((o) -> !validateOrder(o).isValid))
}
}Functii personalizate si module
Creeaza module DataWeave reutilizabile:
// File: src/main/resources/dw/CommonFunctions.dwl
%dw 2.0
// String utilities
fun capitalize(str: String): String =
upper(str[0]) ++ lower(str[1 to -1])
fun toTitleCase(str: String): String =
str splitBy " " map capitalize($) joinBy " "
fun truncate(str: String, maxLen: Number): String =
if (sizeOf(str) > maxLen)
str[0 to maxLen - 4] ++ "..."
else
str
// Date utilities
fun formatDateISO(date: Date): String =
date as String {format: "yyyy-MM-dd'T'HH:mm:ss'Z'"}
fun daysBetween(date1: Date, date2: Date): Number =
(date2 - date1) as Number {unit: "days"}
fun isBusinessDay(date: Date): Boolean = do {
var dayOfWeek = date as String {format: "E"}
---
!(dayOfWeek == "Sat" or dayOfWeek == "Sun")
}
// Currency formatting
fun formatCurrency(amount: Number, currency: String): String = do {
var symbols = {
"USD": "\$",
"EUR": "€",
"GBP": "£"
}
---
(symbols[currency] default currency ++ " ") ++
(amount as String {format: "#,##0.00"})
}
// UUID generation (simplified)
fun generateId(prefix: String): String =
prefix ++ "-" ++ uuid() splitBy "-" joinBy ""
// Null-safe navigation
fun safeGet(obj: Object, path: String): Any =
path splitBy "." reduce ((key, acc = obj) ->
if (acc is Object) acc[key] else null
)Utilizarea modulelor personalizate
%dw 2.0
output application/json
// Import custom module
import * from dw::CommonFunctions
var customerData = {
name: "john doe",
email: "JOHN.DOE@EXAMPLE.COM",
description: "This is a very long description that should be truncated for display purposes in the UI",
orderDate: "2025-01-15" as Date,
orderTotal: 1234.56
}
---
{
displayName: toTitleCase(customerData.name),
email: lower(customerData.email),
shortDescription: truncate(customerData.description, 50),
orderDateFormatted: formatDateISO(customerData.orderDate),
daysSinceOrder: daysBetween(customerData.orderDate, now()),
totalFormatted: formatCurrency(customerData.orderTotal, "USD"),
transactionId: generateId("TXN")
}Optimizarea performantei
Optimizeaza DataWeave pentru payload-uri mari:
%dw 2.0
output application/json
// Use streaming for large files
// output application/json streaming=true
var largeDataset = (1 to 10000) as Array map {
id: $,
value: random() * 1000,
category: ["A", "B", "C", "D"][mod($, 4)]
}
---
{
// GOOD: Use reduce instead of multiple iterations
optimizedAggregation: largeDataset reduce ((item, acc = {
count: 0,
sum: 0,
categories: {}
}) -> {
count: acc.count + 1,
sum: acc.sum + item.value,
categories: acc.categories ++ {
(item.category): (acc.categories[item.category] default 0) + 1
}
}),
// GOOD: Filter early, transform later
filteredFirst: largeDataset
filter ($.value > 500) // Reduce dataset size first
map { // Then transform
id: $.id,
roundedValue: round($.value)
},
// GOOD: Use distinctBy with specific field
uniqueCategories: largeDataset.category distinctBy $,
// Limit output for previews
preview: largeDataset[0 to 9]
}
/*
Sfaturi de performanta:
1. Foloseste streaming pentru fisiere > 10MB
2. Filtreaza inainte de a transforma
3. Evita loop-urile imbricate - foloseste groupBy si reduce
4. Foloseste evaluarea lazy cu defer
5. Minimizeaza conversiile de tip in interiorul loop-urilor
*/Concluzie
Abordarea functionala a DataWeave face transformarile complexe elegante si usor de intretinut. Stapaneste conversia tipurilor, operatiile pe array-uri si functiile personalizate pentru a gestiona orice scenariu de integrare. Foloseste module pentru reutilizare si respecta bunele practici de performanta pentru workload-uri de productie. Cu aceste tehnici, poti aborda orice provocare de transformare in MuleSoft.
Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →