jsonnet Transform
Execute a Jsonnet expression to transform and combine input data. This transform uses go-jsonnet, a native Go implementation of Jsonnet.
Usage
node:
config:
from_file: config.json
env:
from_file: env.yaml
result:
to_file: result.json
transform:
jsonnet: |
{
name: config.name,
env: env.environment,
merged: std.mergePatch(config, env)
}
Variables
All nodes are available as local variables in the Jsonnet expression.
Single-Document Nodes
For nodes with one document, access directly:
// Node with single file
config
// Access nested fields
config.database.host
// Access array elements
users[0].name
Multi-Document Nodes
For nodes with multiple documents, access by document name:
node:
inputs:
from_file:
- base.json # Document "base"
- override.json # Document "override"
result:
transform:
jsonnet: |
{
base: inputs.base,
override: inputs.override,
merged: std.mergePatch(inputs.base, inputs.override)
}
Hybrid Nodes
In hybrid nodes (nodes with both from_* and transform), the input data is available as _self_:
node:
config:
from_file: base.json
to_file: output.json
transform:
jsonnet: |
_self_ + { extra: "added" }
Note: The variable is named _self_ (not self) because self is a reserved keyword in Jsonnet.
Jsonnet Features
Object Merge
Use + for shallow merge or std.mergePatch for deep merge:
transform:
jsonnet: |
// Shallow merge (later object wins for conflicting keys)
base + override
// Deep merge (recursive merge of nested objects)
std.mergePatch(base, override)
Array Operations
transform:
jsonnet: |
{
count: std.length(data.items),
sum: std.foldl(function(a, b) a + b, data.items, 0),
doubled: [x * 2 for x in data.items],
filtered: [x for x in data.items if x > 10]
}
Conditionals
transform:
jsonnet: |
{
logLevel: if config.debug then "debug" else "info",
settings: if config.env == "production"
then { ssl: true, timeout: 30 }
else { ssl: false, timeout: 60 }
}
Functions
transform:
jsonnet: |
local double(x) = x * 2;
local greet(name) = "Hello, " + name;
{
doubled: double(config.value),
greeting: greet(config.name)
}
String Formatting
transform:
jsonnet: |
{
url: "https://%s:%d" % [config.host, config.port],
message: "Hello, %(name)s!" % config
}
Standard Library Functions
Jsonnet includes a rich standard library. Common functions:
| Function |
Example |
Description |
std.mergePatch(a, b) |
Deep merge two objects |
|
std.length(arr) |
Array/string/object length |
|
std.map(f, arr) |
Map function over array |
|
std.filter(f, arr) |
Filter array by predicate |
|
std.foldl(f, arr, init) |
Fold/reduce array |
|
std.sort(arr) |
Sort array |
|
std.uniq(arr) |
Remove duplicates |
|
std.objectFields(obj) |
Get object keys |
|
std.objectValues(obj) |
Get object values |
|
std.toString(v) |
Convert to string |
|
std.parseInt(s) |
Parse string to int |
|
std.manifestJsonEx(v, indent) |
Format as JSON string |
|
For a complete reference, see the Jsonnet standard library documentation.
Comparison with Other Transforms
| Aspect |
jsonnet |
jq |
gotmpl_inline |
| Syntax |
Jsonnet |
jq |
Go templates |
| Merge |
+ / std.mergePatch |
* |
Manual |
| Functions |
First-class |
Built-in |
Sprig |
| Imports |
Supported |
Not supported |
Not supported |
| Comments |
// and /* */ |
# |
{{/* */}} |
| Best for |
Complex logic, reuse |
Data transformation |
Text generation |
Use jsonnet when:
- You need complex logic with functions and local variables
- You want to reuse code via imports (when using jsonnet files)
- You prefer a more programming-language-like syntax
- You're already familiar with Jsonnet
Use jq when:
- You need powerful array filtering and mapping
- You want a more concise syntax for simple transformations
- You're already familiar with jq
Examples
Basic Transformation
node:
source:
from_file: config.json
minimal:
to_file: minimal.json
transform:
jsonnet: |
{
appName: source.application.name,
version: source.application.version,
port: source.server.port
}
Environment-Specific Config
node:
base:
from_file: base.yaml
env:
from_file: production.yaml
config:
to_file: config.json
transform:
jsonnet: |
std.mergePatch(base, env) + {
generated: true,
timestamp: std.extVar("timestamp") // If needed
}
Processing Arrays
node:
users:
from_file: users.json
report:
to_file: report.json
transform:
jsonnet: |
local adults = [u for u in users if u.age >= 18];
local names = [u.name for u in users];
{
total: std.length(users),
adults: std.length(adults),
names: std.sort(names)
}
Error Handling
Common errors and solutions:
| Error |
Cause |
Solution |
| "Expected token IDENTIFIER but got self" |
Using self as variable |
Use _self_ for hybrid node data |
| "Unknown variable" |
Referencing undefined node |
Check node name spelling |
| "Field does not exist" |
Accessing missing field |
Use std.get(obj, field, default) |