Transmute Developer Guide v0.5.1
Transmute is a high-performance, DataWeave-compatible JSON transformation engine with an interactive browser Playground. Write expressive data transformations using a clean DSL — filter, map, reduce, groupBy, and 80+ built-in functions.
DataWeave Compatible
1265 Tests Pass
Enterprise Ready
Quickstart
Open the Playground, paste your JSON into the Payload panel, then write a DSL expression in the Script editor.
Copy output application/json
---
payload.users filter ($.age > 25) map $.name
💡 Auto-execution The Script editor re-runs 350ms after every keystroke. Press Ctrl +Enter to run immediately.
▶ Try in Playground
Playground UI Tour
Located on the left . Contains the Payload panel (your input JSON) and optional named Variables .
Control Purpose
Type dropdown (JSON ▾) Input format — JSON, TEXT, XML, CSV, YAML. Real-time validation per type.
✓ / ✗ badge Green = valid, red = invalid with inline error message
+ Add (Variables) Named variables (JSON or TEXT) — referenced by name in scripts
ℹ️ Smart validation Selecting CSV while the payload is JSON immediately shows an error — the validator detects JSON structure ({ or [) and rejects it for CSV/XML types.
② Script Editor Updated
A unified code editor in the center with live line numbers and syntax highlighting .
Element Description
Line numbers Auto-updated gutter, syncs scroll position
Syntax highlighting Keywords (purple), operators (blue), payload refs (orange), strings (green)
JSON badgeAuto-detected from output application/json in your script
Status dot Gray=idle, amber=running, green=OK, red=error
💡 Full-script paste You can paste a complete script (including output header and ---) directly into the editor — it detects and uses it as-is.
③ Output Panel
Control Purpose
Format dropdown Changing it re-runs the transformation with the new output type
Copy Copy output to clipboard
Pretty toggle Indented vs compact output
Metrics bar Execution time, streaming mode, row count
④ Autocomplete New
As you type, a suggestion popup appears. The first item is always pre-selected — press Enter immediately to accept it.
Badge Type Examples
fn Built-in function upper(, sizeOf(, flatten(
op Infix operator filter, map, groupBy
kw Keyword payload, if, var, fun
f Payload field Extracted from current JSON (payload.users.name)
v Script variable From var x = ... in your script
Navigate with ↑ ↓ , accept with Enter or Tab , dismiss with Esc .
⑤ Log Viewer
Collapsible panel at the bottom — shows output from log() and trace() calls. Click ▶ Logs to expand.
Script Structure
Copy // Header: output directive + variable/function definitions
output application/json
var active = payload.users filter $.age > 25
fun greet(name) = "Hello, " ++ name
---
// Body: the expression to evaluate
{count: sizeOf(active), msg: greet(active[0].name)}
Directive Description
output application/jsonOutput format. Also: text/plain, application/csv, application/xml
import fn from moduleModule imports (all functions pre-registered; accepted and ignored)
var name = exprBind variable — available in body and in later var declarations
fun name(params) = bodyDefine reusable function — supports type annotations & default values
---Separator between header and body
Field Access
Copy payload.name // dot access
payload["first name" ] // bracket key
payload."first name" // dot + quoted key
payload?.address?.city // safe navigation (null if missing)
payload.users[0] // 0-based index
payload.users[-1] // last element
payload.users[0 to 2] // range slice
payload.card[-4 to -1] // last 4 chars of a string
payload.users[*].name // wildcard: all names
payload..name // descendants: all name values at any depth
xml.employee.@id // XML attribute access (@ prefix)
xml.employee.@id // Stored as "@id" key in parsed XML map
ℹ️ XML Attributes: When XML input is parsed, attributes become @attrName keys. Access them with .@attr syntax, e.g. payload.employee.@id. Produce XML output with output application/xml in the script header.
Operators
Arithmetic & Concatenation
Operator Description Example
+Add numbers; concat when non-numeric 5 + 3
-Subtract; or remove field from object payload - "password"
* / %Multiply, divide, modulo $ mod 2 == 0
++Concat arrays, objects, or strings [1] ++ [2], "a" ++ "b"
--Remove from array or object [1,2,3] -- [2]
Comparison & Logical
Operator Example
== != > < >= <=Numeric strings auto-coerced: "26" > 25
and or not !$.active and $.age > 18
containspayload contains 3
startsWith / endsWith"file.csv" endsWith ".csv"
~=Type-coercing equality — key ~= "1" matches key "1" or 1
matches$.code matches /^[A-Z]+/
is / as$ is String, "42" as Number
default$.name default "N/A"
Object shorthand New
Copy // IDENTIFIER: expr creates {IDENTIFIER: expr}
result: payload.users filter $.active
// same as: {result: payload.users filter $.active}
filter
Copy payload filter $.age > 25
payload filter ((item, index) -> item.active and index > 0)
payload filter isEven ($)
map
Copy payload map $.name
payload map ($ * 2)
payload map (value, index) -> {id: index, v: value}
payload map (v,k) -> {num: v.num last 3}
reduce
Copy payload reduce ($$ + $)
payload reduce ((item, acc = 0) -> acc + item)
payload reduce ((item, acc = {}) -> acc ++ {(item.name): item.value})
groupBy
Copy payload groupBy $.dept
(payload groupBy $.dept) mapObject (members, dept) ->
{(dept): members map $.name}
mapObject
$ = value, $$ = key, $$$ = index
Copy payload mapObject (value, key) -> {(key): upper (value)}
payload mapObject {(upper ($$)): $}
payload mapObject (v, k, i) -> {(i): {key: k, val: v}} // $$$ = index
filterObject
Copy payload filterObject ($ != null )
payload filterObject ((value, key) -> key != "password" )
payload filterObject ((v, k, i) -> k ~= "id" ) // ~= type-coercing equal
let / fun Enhanced
Copy // var in header (preferred)
output application/json
var active = payload.users filter $.active
var total = sizeOf (active)
---
{total: total, users: active}
// fun with typed params + default + --- separator
fun factorial(n:Number, acc:Number=1) =
(if (n <= 1) acc else factorial(n-1, acc*n))
---
factorial(payload)
if / else / match
Copy if (payload.status == "OK" ) 200 else 500
payload.score > 90 ? "A" : "B"
payload match {
case "A" -> 1
case s is String -> upper (s)
case n is Number -> n * 2
else -> null
}
Lambdas
Copy $ -> $ * 2
(x) -> x * 2
(value, index) -> {id: index, v: value}
(item, acc = 0) -> acc + item
⚠️ Nested infix operators Wrap inner map/filter in parens: (cus) -> (cus.orders map {...})
String Functions
fn capitalize(str) Capitalize first
fn trim(str) Strip whitespace
fn replace(str, from, to) Replace pattern
op str splitBy delim Split to array
op arr joinBy delim Join to string
op str startsWith s Prefix check
op str endsWith s Suffix check
fn substringAfter(str, d) After delimiter
fn substringBefore(str, d) Before delimiter
fn substringBeforeLast(s, d) Before last delimiter
fn underscore(str) camelCase to snake_case
fn padLeft(str, n, c) Left-pad to length
fn padRight(str, n, c) Right-pad to length
fn repeat(str, n) Repeat n times
fn charCode(char) ASCII code
fn fromCharCode(n) Char from ASCII
fn sizeOf(str) String length
fn lines(str) Split into lines
fn words(str) Split into words
String interpolation
Copy "Hello, $(payload.name)! Age: $(payload.age)"
Array Functions
fn flatten(arr) Flatten nested arrays
op arr flatMap fn Map then flatten
op arr distinctBy fn? Remove duplicates
op arr orderBy fn? Sort ascending
fn average(arr) Average of array
fn sizeOf(arr) Array length
fn first(arr) First element
op arr maxBy fn Element with max key
op arr minBy fn Element with min key
op arr countBy fn Count matching
fn take(arr, n) First n elements
fn drop(arr, n) Skip first n
fn reverse(arr) Reverse array
fn isEmpty(arr) True if empty
fn randomInt(n) Random 0..n-1
Object Functions
fn keysOf(obj) Array of keys
fn valuesOf(obj) Array of values
op obj pluck fn Object to array
fn hasKey(obj, key) Check key exists
fn merge(obj1, obj2) Deep merge
fn pick(obj, keys) Pick specific keys
Math Functions
Function Example
abs(n)abs(-5) → 5
ceil(n)ceil(1.2) → 2
floor(n)floor(1.9) → 1
round(n, scale?)round(3.567, 2) → 3.57
sqrt(n)sqrt(9) → 3
pow(base, exp)pow(2,10) → 1024
sum(arr)sum([1,2,3]) → 6
max(arr)max([5,1,9]) → 9
min(arr)min([5,1,9]) → 1
average(arr)average([2,4]) → 3
💡 Integer output Whole-number results print without .0 — sum([10,20,30]) outputs 60, not 60.0.
Type & Logic Functions
Function Description
isEven(n) / isOdd(n)Numeric parity check
isAlpha(s) / isNumeric(s)String character type
isNull(v) / isEmpty(v)Null / empty check
isString(v) / isNumber(v) / isArray(v)Runtime type checks
typeof(v)Returns type as string
v as Number / String / BooleanType coercion
v is String / Number / ArrayType check operator
v default fallbackReturn fallback if v is null
Crypto Functions New
Cryptographic hashing, HMAC, and symmetric encryption via dw::Crypto-compatible built-ins.
Function Description
MD5(str)Hex-encoded MD5 hash
SHA1(str)Hex-encoded SHA-1 hash
SHA256(str)Hex-encoded SHA-256 hash
SHA512(str)Hex-encoded SHA-512 hash
HMACWith(content, secret, alg)Hex HMAC — alg: "HmacSHA256", "HmacSHA512", "HmacMD5"
HMACBinary(content, secret, alg)Base64 HMAC (binary output)
aesEncrypt(content, key)AES-GCM encrypt → Base64 (IV prepended, authenticated)
aesDecrypt(ciphertext, key)AES-GCM decrypt → plaintext string
Copy // Hashing
MD5 ("hello" ) // "5d41402abc4b2a76b9719d911017c592"
SHA256 ("hello" ) // "2cf24dba5fb0a30e..."
// HMAC
HMACWith ("payload" , "secret" , "HmacSHA256" )
// AES-256-GCM round-trip
let cipher = aesEncrypt ("hello world" , "mySecretKey" )
aesDecrypt (cipher, "mySecretKey" ) // "hello world"
⚠️ Security: aesEncrypt uses AES-256-GCM with a random IV per call (NIST SP 800-38D). The IV is prepended to the ciphertext — only aesDecrypt with the same key can recover the plaintext.
Tree Functions New
Recursive tree traversal helpers from dw::util::Tree.
Function Description
mapLeafValues(value, mapper)Apply mapper(v) to every scalar leaf in the tree; preserves object/array structure
filterTree(value, criteria)Remove entries/elements where criteria(v, path) returns false; recurses into all nodes
Copy // Uppercase every string leaf
mapLeafValues ({a: "hello" , b: {c: "world" }}, (v) -> upper (v))
// {a: "HELLO", b: {c: "WORLD"}}
// Remove empty fields at any depth
filterTree (payload, (v, path) -> !isEmpty (v as String ))
Date & Time Functions
Function Description
now()Current datetime (ISO 8601)
formatDate(dt, fmt)Format: "yyyy-MM-dd"
dateParse(str, fmt)Parse string to datetime
year(dt) month(dt) day(dt)Extract components
daysBetween(d1, d2)Days between two dates
plusPeriod(dt, n, unit)Add: "days", "months", "years"
Pattern: Dynamic Keys
Copy {(payload .key): payload .value}
payload map (value, index) -> {("item" ++ index): value}
(payload groupBy $.customer)
mapObject (orders, cust) -> {(cust): sizeOf (orders)}
Pattern: Nested Maps
Copy flatten (payload .customers map (cus) ->
(cus.orders map {customer: cus.name, orderId: $.id}))
ℹ️ Wrap the inner map expression in parentheses when it appears inside a lambda body.
Pattern: Merge & Join
Copy (payload .arr1 ++ payload .arr2) distinctBy $
payload reduce ($$ ++ $)
payload .metrics reduce ((item, acc={}) ->
acc ++ {(item.name): item.value})
Pattern: Recursion
Copy fun factorial(n:Number, acc:Number=1) =
(if (n <= 1) acc else factorial(n-1, acc*n))
---
factorial(payload) // factorial(6) -> 720
fun recsum(arr, total=0) =
if (isEmpty (arr)) total
else recsum(arr[1 to -1], total + arr[0])
---
recsum(1 to 10) // -> 55
Keyboard Shortcuts
Run transformation Ctrl +Enter
Accept autocomplete Enter or Tab
Navigate suggestions ↑ / ↓
Dismiss autocomplete Esc
Select all in editor Ctrl +A
Copy output Copy button
REST API Reference
Copy POST /api/v1/transform
{
"dsl": "output application/json\n---\npayload filter $.age > 25",
"json": "<employees><employee id=\"1\"><name>Alice</name></employee></employees>",
"inputType": "XML", // JSON (default) | XML | CSV | YAML | TEXT
"pretty": true,
"variables": {"cfg": {"content":"{\"env\":\"prod\"}","type":"JSON"}}
}
Endpoint Method Description
/api/v1/transformPOST Execute DSL transformation
/api/v1/validatePOST Validate DSL syntax only
/api/v1/healthGET Full health check (uptime, memory, JVM)
/api/v1/metricsGET Request counts, memory, threads
/api/v1/infoGET Service info, version, limits
Enterprise Features
Feature Detail
API versioning /api/v1/ prefix; legacy /api/ still works
Rate limiting 120 requests/minute per IP (sliding window)
Request tracing X-Request-ID header — pass your own UUID or one is generated
Security headers X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, CSP, Referrer-Policy
Execution timeout 30 000 ms per transformation (configurable)
Payload size limit 10 MB maximum JSON
Response compression Gzip for responses ≥ 1 KB
CORS All origins; Vary: Origin prevents CDN cache poisoning
Error safety Stack traces never exposed; structured error JSON
Cache-Control no-store, no-cache on all /api/* responses
HSTS Strict-Transport-Security: max-age=31536000 on HTTPS connections
AES encryption AES-256-GCM with random IV (NIST SP 800-38D) — not ECB
XFF rate-limit bypass X-Forwarded-For ignored by default; opt-in via app.trust-proxy=true
IP counter cleanup Rate-limit map purged every 5 min to prevent unbounded memory growth
Transmute Playground — DataWeave-compatible DSL engine —
Open Playground ▶