Write advanced data transformation logic using JSONNET for complex field mapping scenarios.
JSONNET is a data templating language developed by Google that generates JSON through programmable logic. It's designed for configuration management and data transformation.
Functions
Define reusable transformation logic with parameters
Variables
Store intermediate values using local bindings
Conditionals
Implement branching with if-then-else
Standard Library
Built-in functions for strings, arrays, objects, math
Imports
Include external libraries for code reuse
Learn More
For complete language reference, visit the Official JSONNET Documentation →
Every custom action executes within a special context providing access to data and utilities:
Prop
Type
Description
$.inputobjectComplete input payload containing work order or external record data. Access nested fields with dot notation: `$.input.workorder.id`
$.utilobjectUtility library with helper functions for common transformations (field lookup, date conversion, mapping)
stdobjectJSONNET standard library with array, string, math, and type functions
The simplest custom action directly accesses input fields:
{
full_name: $.util.lookup_field($.input, 'assignee.user.first_name', '') + ' ' +
$.util.lookup_field($.input, 'assignee.user.last_name', '')
}Result: Concatenates first and last name with space, using safe defaults for missing fields.
The Integration Broker provides comprehensive utility functions optimized for field mappings.
Use if-then-else with local variables for complex decisions:
{
priority:
local hours = $.util.lookup_field($.input, 'time_logs.hours', 0);
local is_urgent = $.util.lookup_field($.input, 'priority', '') == 'urgent';
if is_urgent && hours > 2 then
"Critical"
else if is_urgent then
"High"
else if hours > 4 then
"Medium"
else
"Low"
}Common string operations using the standard library:
{
// Uppercase
title_upper: std.asciiUpper($.util.lookup_field($.input, 'workorder.title', '')),
// Extract email domain
email_domain:
local email = $.util.lookup_field($.input, 'user.email', '');
local parts = std.split(email, '@');
if std.length(parts) > 1 then parts[1] else '',
// Remove whitespace
clean_text: std.stripChars($.util.lookup_field($.input, 'description', ''), ' \t\n')
}Filter, map, and find operations:
{
// Filter array
high_value_expenses:
local all_expenses = $.input.pay.expense.results;
std.filter(function(e) e.amount > 100, all_expenses),
// Map array
expense_amounts:
std.map(
function(e) e.amount,
$.input.pay.expense.results
),
// Find element
first_travel_expense:
local expenses = $.input.pay.expense.results;
local travel = std.filter(
function(e) e.category.name == 'Travel',
expenses
);
if std.length(travel) > 0 then travel[0] else null
}Round, format, and calculate percentages:
{
// Round to 2 decimal places
rounded_cost: std.round($.input.pay.amount * 100) / 100,
// Format as currency
formatted_amount:
local amount = $.util.lookup_field($.input, 'pay.amount', 0);
"$" + std.toString(std.round(amount * 100) / 100),
// Percentage calculation
completion_rate:
local completed = $.util.lookup_field($.input, 'tasks_completed', 0);
local total = $.util.lookup_field($.input, 'tasks_total', 1);
std.round((completed / total) * 100) + "%"
}Build nested objects from flat data:
{
// Build nested object
contact_info: {
name: $.util.lookup_field($.input, 'assignee.user.first_name', '') + ' ' +
$.util.lookup_field($.input, 'assignee.user.last_name', ''),
email: $.util.lookup_field($.input, 'assignee.user.email', ''),
phone: $.util.lookup_field($.input, 'assignee.user.phone', ''),
address: {
street: $.util.lookup_field($.input, 'location.address1', ''),
city: $.util.lookup_field($.input, 'location.city', ''),
state: $.util.lookup_field($.input, 'location.state', ''),
zip: $.util.lookup_field($.input, 'location.zip', '')
}
}
}Store intermediate values for readability and performance:
{
result:
// Reusable, clear
local hours = $.util.lookup_field($.input, 'time_logs.hours', 0);
local rate = $.util.lookup_field($.input, 'pay.hourly_rate', 0);
local total = hours * rate;
std.round(total * 100) / 100
}{
result:
// Repeated lookups, hard to read
std.round(($.util.lookup_field($.input, 'time_logs.hours', 0) *
$.util.lookup_field($.input, 'pay.hourly_rate', 0)) * 100) / 100
}Prevent null pointer errors with safe defaults:
{
// Safe with defaults
total: $.util.lookup_field($.input, 'amount', 0) +
$.util.lookup_field($.input, 'tax', 0)
}{
// May crash if fields missing
total: $.input.amount + $.input.tax
}Calculate once, use multiple times:
{
result:
local all_expenses = $.input.pay.expense.results;
local total = $.util.sum_expense_field(all_expenses, "amount");
local count = std.length(all_expenses);
{
total: total,
count: count,
average: if count > 0 then total / count else 0
}
}Validate types to prevent runtime errors:
{
safe_division:
local numerator = $.util.lookup_field($.input, 'completed', 0);
local denominator = $.util.lookup_field($.input, 'total', 1);
if std.isNumber(numerator) && std.isNumber(denominator) && denominator != 0 then
numerator / denominator
else
0
}Create a test payload and validate output:
// Test payload
local test_input = {
workorder: {
id: 12345,
title: "Test WO",
pay: { amount: 150.50 }
}
};
// Your transformation
local result = {
id: test_input.workorder.id,
formatted_amount: "$" + std.toString(test_input.workorder.pay.amount)
};
// Output for testing
resultAdd trace statements to inspect values (output appears in integration logs):
{
result:
local value = $.util.lookup_field($.input, 'some.field', 'default');
local traced = std.trace("Field value: " + std.toString(value), value);
// Transformation continues with traced value
traced
}Last updated on