Differences to JSONata

A constraint reads like JSONata, and most differences turn a JSONata habit into a definition error rather than another result.

Different Results

  • Units are converted. deadline <= period works when deadline is in milliseconds and period in microseconds. JSONata on the exported JSON would compare the bare numbers.

  • Default values count. frameSize == 64 holds for a controller that gets 64 from the default of frameSize. The exported JSON leaves defaults out, so JSONata would find no value there.

  • Large integers stay exact. JSONata rounds integers beyond about 9 * 1015, which can matter for 64-bit values such as IDs or masks.

  • A missing value gives no result. x != 1 without an x is neither true nor false, where JSONata says false. Use ?? to choose a fallback: (x != 1) ?? true.

  • ?? and ?: bind tighter than a comparison, as in Swift and Kotlin: x ?? 0 > 5 means (x ?? 0) > 5. JSONata reads 11 = x ?? 11 as (11 = x) ?? 11. Put parentheses around a ?? or ?: next to a comparison, &&, || or ? : and both read the same.

  • ?: takes a boolean, a number or a string. It falls back on an absent value, false, zero or '', as JSONata does. JSONata also tests a list or an object, write $count(x) > 0 or ?? for those.

  • Errors fail the constraint. A division by zero, which JSONata answers with Infinity, or an overflow is a violation. A constraint does not run while a configuration file it reads has errors, those errors are reported on their own.

  • Decimals print in full. 0.1 + 0.2 shows as 0.30000000000000004 in a message, JSONata’s $string rounds it to 0.3.

  • Emoji can sort differently. Text with an emoji or another rare character may sort in another order than in JSONata.

  • $round rounds a half away from zero. $round(2.5) is 3, JSONata rounds a half to the even neighbor, 2.

  • $trim keeps inner whitespace. $trim(' a b ') is 'a b', JSONata also collapses every inner run of whitespace into one space.

Stricter Rules

  • Lists stay lists. JSONata turns a list of one into a single value and an empty list into nothing, here a list is a list of any size. So $exists(tasks) is an error, write $count(tasks) > 0. The other way round, $count, $sum, $min, $max, $average, $distinct, $unique, $every, $some, $single and $filter take only lists, check an optional single value with $exists.

  • Only true and false are conditions. A number, a text or a list is no condition, and a condition that may read a missing value needs a guard first (see Absent Values).

  • $single needs exactly one match. Otherwise the constraint fails, even inside && or ||, and the message lists the matches: Expected exactly one 'tasks.subtasks' match, found 2: 'Sample', 'Filter'.

  • $round takes no precision, write $round(x * 100) / 100.

  • $replace and $split take a string pattern, no regular expression, and $replace no function as its replacement.

  • $filter passes its lambda the element only, not the index and the list.

  • Indexes and ranges are fixed numbers. tasks[priority] is an error, JSONata would pick tasks by position instead of filtering.

  • No array literals inside array literals. JSONata keeps [[1, 2], [3]] nested, here it would be flattened, so it is an error. A path yielding a list joins into the outer list, [tasks.subtasks.name, cores.name] on board.

  • % works where the parent is known when the definition is read. It also works where JSONata has no parent: at the start of a path, %.nominalBitrate, and after a function, $referencedBy(...).%.

Additions

  • C-style operators: !, &&, || and == beside and, or and =. ! binds tighter than a comparison, as in C.

  • Hex numbers such as 0x7FF.

  • Message placeholders: {expr} inside the text, {{ and }} for literal braces, instead of building a text with &.

  • Short functions as an argument of $every, $some, $single, $filter and $noOverlap: $t => body, or $ => body to read the element’s fields directly. function($t){ body } works as well, a function anywhere else does not.

  • Own functions: $unique, $every, $some, $path, $referencedBy, $cfgByDefinition, $noOverlap, $meta, $reference and $def. $isType tests a type, JSONata’s $type returns the JSON type instead.

Left Out

The error for each of these names what to write instead:

  • & to join text, the message placeholders cover it

  • a ? b without an else part

  • **, ~>, null, the transform, sorting with ^(), object constructors, regular expressions, backtick names, comments, and the bindings #$i and @$v

  • the JSONata functions in the table below, and every other one not listed, such as $sqrt

Left-out functions and what replaces them:

Function Replacement

$map

A path step maps, tasks.(runtime / period).

$reduce

An aggregate over a path step folds, $sum(tasks.(runtime / period)).

$sift

Entities are no free-form objects.

$each

Entities are no free-form objects.

$sort

Order-by is not in the constraint language.

$eval

A constraint is checked when the definition is parsed, $eval would defer that.

$type

$isType tests a type by its type path, such as Def::Type.

$match

Regular expressions are not supported yet, constrain a string with pattern in its definition.

$power

Write the product, x * x.

$append

An array literal joins lists, [a, b].

$keys

Entities are no free-form objects.

$lookup

Entities are no free-form objects.

$spread

Entities are no free-form objects.

$merge

Entities are no free-form objects.

$now

A constraint reads no clock, its result depends on the configuration alone.

$millis

A constraint reads no clock, its result depends on the configuration alone.

$boolean

Booleans are strict, compare explicitly, $count(x) > 0.

$string

A message placeholder renders any value, {x}.

$join

A message placeholder joins a list with commas, {tasks.name}.

$assert

The message of a failing assert reports the violation.

$error

The message of a failing assert reports the violation.