Operators
JSON-e defines a bunch of operators. Each is represented as an object with a
property beginning with $
. This object can be buried deeply within the
template. Some operators take additional arguments as properties of the same
object.
Truthiness
Many values can be evaluated in context where booleans are required, not just booleans themselves. JSON-e defines the following values as false. Anything else will be true.
template: {$if: 'a || b || c || d || e || f', then: "uh oh", else: "falsy" }
context: {a: null, b: [], c: {}, d: "", e: 0, f: false}
result: "falsy"
$eval
The $eval
operator evaluates the given expression and is replaced with the
result of that evaluation. Unlike with string interpolation, the result need
not be a string, but can be an arbitrary data structure.
template: {config: {$eval: 'settings.staging'}}
context:
settings:
staging:
transactionBackend: mock
production:
transactionBackend: customerdb
result:
{config: {transactionBackend: 'mock'}}
The expression syntax is described in more detail below.
Note that $eval
's value must be a string. "Metaprogramming" by providing a
calculated value to eval is not allowed. For example, {$eval: {$eval: "${var1} + ${var2}"}}
is not valid JSON-e.
$json
The $json
operator formats the given value as JSON with sorted keys. It does
not evaluate the value (use $eval
for that). While this can be useful in some
cases, it is an unusual case to include a JSON string in a larger data
structure.
Parsing the result of this operator with any compliant JSON parser will give the same results. However, the encoding may differ between implementations of JSON-e. For example, numeric representations or string escapes may differ between implementations.
template: {$json: [a, b, {$eval: 'a+b'}, 4]}
context: {a: 1, b: 2}
result: '["a","b",3,4]'
$if
- then
- else
The $if
operator supports conditionals. It evaluates the given value, and
replaces itself with the then
or else
properties. If either property is
omitted, then the expression is omitted from the parent object.
template: {key: {$if: 'cond', then: 1}, k2: 3}
context: {cond: true}
result: {key: 1, k2: 3}
template: {$if: 'x > 5', then: 1, else: -1}
context: {x: 10}
result: 1
template: [1, {$if: 'cond', else: 2}, 3]
context: {cond: false}
result: [1,2,3]
template: {key: {$if: 'cond', then: 2}, other: 3}
context: {cond: false}
result: {other: 3}
$flatten
The $flatten
operator flattens an array of arrays into one array.
template: {$flatten: [[1, 2], [3, 4], [5]]}
context: {}
result: [1, 2, 3, 4, 5]
$flattenDeep
The $flattenDeep
operator deeply flattens an array of arrays into one array.
template: {$flattenDeep: [[1, [2, [3]]]]}
context: {}
result: [1, 2, 3]
$fromNow
The $fromNow
operator is a shorthand for the built-in function fromNow
. It
creates a JSON (ISO 8601) datestamp for a time relative to the current time
(see the now
builtin, below) or, if from
is given, relative to that time.
The offset is specified by a sequence of number/unit pairs in a string.
Whitespace is ignored, but the units must be given in ordre from largest to
smallest. To produce a time in the past, prefix the string with -
. A +
prefix is allowed as a redundant way to specify a time in the future.
template: {$fromNow: '2 days 1 hour'}
context: {}
result: '2017-01-21T17:27:20.974Z'
template: {$fromNow: '1 hour', from: '2017-01-19T16:27:20.974Z'}
context: {}
result: '2017-01-19T17:27:20.974Z'
The available units, including useful shorthands, are:
years, year, yr, y
months, month, mo
weeks, week, wk, w
days, day, d
hours, hour, hr, h
minutes, minute, min, m
seconds, second, sec, s
$let
The $let
operator evaluates an expression using a context amended with the
given values. It is analogous to the Haskell where
clause.
template: {$let: {ts: 100, foo: 200},
in: [{$eval: "ts+foo"}, {$eval: "ts-foo"}, {$eval: "ts*foo"}]}
context: {}
result: [300, -100, 20000]
The $let
operator here added the ts
and foo
variables to the scope of
the context and accordingly evaluated the in
clause using those variables
to return the correct result.
An expression like {$let: {$eval: "extraVariables"}, in : ..}
is supported. As long as
the value of $let
evaluates to an object with valid key(s), the values of which are
evaluated.
template: {$let: {$if: something == 3, then: {a: 10, b: 10}, else: {a: 20, b: 10}},
in: {$eval: 'a + b'}}
context: {'something': 3}
result: 20
template: {$let: {"b": {$eval: "a + 10"}},
in: {$eval: "a + b"}}
context: {a: 5}
result: 20
template: {$let: {"first_${name}": 1, "second_${name}": 2},
in: {$eval: "first_prize + second_prize"}}
context: {name: "prize"}
result: 3
$map
The $map
operator evaluates an expression for each value of the given array or object,
constructing the result as an array or object of the evaluated values.
Over Arrays
When given an array, $map
always returns an array.
template:
$map: [2, 4, 6]
each(x): {$eval: 'x + a'}
context: {a: 1}
result: [3, 5, 7]
The each
function can define two variables, in which case the second is the 0-based index of the element.
template:
$map: [2, 4, 6]
each(x,i): {$eval: 'x + a + i'}
context: {a: 1}
result: [3, 6, 9]
Over Objects
When given an object, $map
always returns an object.
The each
function defines variables for the value and key, in that order.
It must evaluate to an object for each item.
These objects are then merged, with later keys overwriting earlier keys, to produce the final object.
template:
$map: {a: 1, b: 2, c: 3}
each(v,k): {'${k}x': {$eval: 'v + 1'}}
context: {}
result: {ax: 2, bx: 3, cx: 4}
If each
is defined to take only one variable, then that variable is an object with properties key
and val
.
template:
$map: {a: 1, b: 2, c: 3}
each(y): {'${y.key}x': {$eval: 'y.val + 1'}}
context: {}
result: {ax: 2, bx: 3, cx: 4}
$reduce
The $reduce
operator evaluates an expression with each value of the given array and
the result of the prior expression, reducing the array into a single JSON value.
This operation is sometimes called fold
, accumulate
, aggregate
, or inject
.
An initial result is passed as the accumulator for the first evaluation of the expression.
The each
function defines the accumulated, or prior result, and the current value. The result of
this function will be passed as the accumulated to the next call of each
.
template:
$reduce: [{name: Apple, price: 1}, {name: Orange, price: 0.75}, {name: Pear, price: 1.1}]
initial: 0
each(acc, v): {$eval: 'acc + v.price'}
context: {}
result: 2.85
The each
function can define three variables, in which case the third is the 0-based index of the element.
template:
$reduce: [2, 5, 8]
initial: 0
each(acc, v, i): {$eval: 'acc + v * 10 ** i'}
context: {}
result: 852
$find
The $find
operator evaluates an expression for each value of the given array.
returning the first value for which the expression evaluates to true
.
If there are no matches the result is either null
or if used within an object or array, omitted
from the parent object.
template:
$find: [2, 4, 6]
each(x): x == 4
context: {}
result: 4
Using context variables:
template:
$find: [2, 4, 6]
each(x): a == x
context: {a: 4}
result: 4
Omitting from parent:
template:
a: 1
b:
$find: [2, 4, 6]
each(x): b == x
context:
b: 3
result:
a: 1
The each
function can define two variables, in which case the second is the 0-based index of the element.
template:
$find: [2, 4, 6]
each(x,i): i == 2
context: {}
result: 6
$match
The $match
operator is not dissimilar to pattern matching operators. It gets
an object, in which every key is a string expression evaluating to true
or false
based on the context. Keys are evaluated in lexical order, and the
result is an array containing values corresponding to keys that evaluated to
true
. If there are no matches, the result is an empty array.
template:
$match:
"c > 10": "cherry"
"b > 10": "banana"
"a > 10": "apple"
context: {a: 200, b: 3, c: 19}
result: ["apple", "cherry"]
template: {$match: {"x < 10": "tens"}}
context: {x: 10}
result: []
$switch
The $switch
operator behaves like a combination of the $if
and
$match
operator for more complex boolean logic. It gets an object,
in which every key is a string expression(s), where at most one must
evaluate to true
and the remaining to false
based on the context.
The result will be the value corresponding to the key that were
evaluated to true
or optionally the fallback $default
value.
If there are no matches, and no $default
fallback is provided, the
result is either null or if used within an object or array, omitted
from the parent object.
template: {$switch: {"x == 10": "ten", "x == 20": "twenty"}}
context: {x: 10}
result: "ten"
template: {$switch: {"x < 10": 1}}
context: {x: 10}
result: null
template: {a: 1, b: {$switch: {"x == 10 || x == 20": 2, "x > 20": 3}}}
context: {x: 10}
result: {a: 1, b: 2}
template: {a: 1, b: {$switch: {"x == 1": 2, "x == 3": 3}}}
context: {x: 2}
result: {a: 1}
template: [1, {$switch: {"x == 2": 2, "x == 10": 3}}]
context: {x: 2}
result: [1, 2]
template: [0, {$switch: {'cond > 3': 2, 'cond == 5': 3}}]
context: {cond: 3}
result: [0]
template: [0, {$switch: {'cond > 3': 2, 'cond == 5': 3, $default: 4}}]
context: {cond: 1}
result: [4]
$merge
The $merge
operator merges an array of objects, returning a single object
that combines all of the objects in the array, where the right-side objects
overwrite the values of the left-side ones.
template: {$merge: [{a: 1, b: 1}, {b: 2, c: 3}, {d: 4}]}
context: {}
result: {a: 1, b: 2, c: 3, d: 4}
$mergeDeep
The $mergeDeep
operator is like $merge
, but it recurses into objects to
combine their contents property by property. Arrays are concatenated.
template:
$mergeDeep:
- task:
payload:
command: [a, b]
- task:
extra:
foo: bar
- task:
payload:
command: [c]
context: {}
result:
task:
extra:
foo: bar
payload:
command: [a, b, c]
$sort
The $sort
operator sorts the given array. It takes a by(var)
property which
should evaluate to a comparable value for each element. The by(var)
property
defaults to the identity function.
The values sorted must all be of the same type, and either a number or a string.
template:
$sort: [{a: 2}, {a: 1, b: []}, {a: 3}]
by(x): 'x.a'
context: {}
result: [{a: 1, b: []}, {a: 2}, {a: 3}]
The sort is stable:
template:
$sort: ["aa", "dd", "ac", "ba", "ab"]
by(x): 'x[0]'
context: {}
# stable: all "a" strings remain in the same order relative to one another.
result: ["aa", "ac", "ab", "ba", "dd"]
$reverse
The $reverse
operator simply reverses the given array.
template: {$reverse: [3, 4, 1, 2]}
context: {}
result: [2, 1, 4, 3]
Escaping operators
All property names starting with $
are reserved for JSON-e.
You can use $$
to escape such properties:
template: {$$reverse: [3, 2, {$$eval: '2 - 1'}, 0]}
context: {}
result: {$reverse: [3, 2, {$eval: '2 - 1'}, 0]}