Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Command-line interface

The jlens binary evaluates, checks, explains and inspects JSONata expressions.

jlens eval      <EXPR> | -f FILE  [-i INPUT | --no-input] [--ndjson] [--filter]
                [-b BINDINGS] [-c | -r | --json] [--timeout MS] [--max-depth N]
                [--max-string-length BYTES] [--max-array-length N] [--max-result-size BYTES]
                [--now TIME] [--seed N]
                [--explain | --audit json|text] [--audit-level decisions|full] [--trace-log FILE]
jlens check     <EXPR> | -f FILE...  [--json] [--deny-warnings] [--schema SCHEMA]
                [--config FILE] [--path FILE]
jlens test      FILE...  [--json] [--timeout MS]
                [--max-string-length BYTES] [--max-array-length N] [--max-result-size BYTES]
jlens functions [NAME] [--json]
jlens errors    [CODE] [--json]
jlens schema infer    SAMPLES...  [--each] [--overrides FILE] [-o SCHEMA]
jlens schema complete <EXPR> | -f FILE  [--schema SCHEMA] [--cursor N] [--json]
jlens ast       <EXPR> | -f FILE  [--tokens | --raw] [--recover]

Every command takes the expression either as an argument or from a file with -f. Prefer -f for anything longer than a one-liner. Shells interpret $, " and * differently, and Windows shells (cmd.exe, PowerShell) in particular make quoting expressions awkward. Glob patterns in jlens check -f, jlens test and jlens schema infer are expanded by jlens itself, so they work in every shell.

Exit codes

CodeMeaning
0Success. check may still have printed warnings (unless --deny-warnings)
1The expression has errors: a syntax error, an evaluation error, or (with --ndjson) at least one line that failed; test: at least one case failed
2Usage or I/O problem: unreadable file, invalid JSON input, an invalid case file, bad arguments, a terminal on standard input

Writing to a closed pipe, as in jlens functions | head, ends quietly with code 0.

For scripts and agents

jlens keeps a contract with the programs that run it, recorded in ADR 0008:

  • Standard output is data: results, JSON from --json, catalogs. Standard error is for people: rendered diagnostics, summaries, usage errors. Never parse standard error.
  • Decide on the exit code (see the table above), not on whether anything was printed: an undefined result prints nothing and still exits with 0.
  • Match errors by code (S0202, T2001, D1012…), never by message. Codes are listed in the error reference.
  • The format never depends on the terminal. A command prints the same thing when an agent runs it and when a person copies it into a shell. Ask for machine output with a flag.
  • Pass the expression with -f FILE and the input with -i FILE or --no-input. This avoids shell quoting. An eval that would read its input from a terminal fails with code 2 instead of waiting.
  • Use eval --json to get every outcome, errors included, as one line of JSON on standard output (see below), and check --json for the diagnostics of every file as one JSON array.
  • Test the expressions you write with jlens test and a file of cases, and read the failures from test --json (see below).
  • Fix the clock and the seed with --now and --seed when a result depends on $now(), $millis(), $random() or $shuffle(), so that the same command prints the same bytes when it is run again.

jlens eval

Evaluates the expression against a JSON document and prints the result. The document comes from -i FILE, or from standard input. An undefined result prints nothing. When standard input is a terminal and neither -i nor --no-input is given, eval fails with exit code 2 instead of waiting for a document.

$ jlens eval -f fixtures/shipping-rule.jsonata -i fixtures/online-store-order.json
{
  "action": "FREE_SHIPPING",
  "code": "OK_200"
}
OptionEffect
-i, --input FILEThe input document (default: standard input)
--no-inputEvaluate without an input document ($ is undefined)
-b, --bindings FILEA JSON object whose fields are bound as variables: {"limit": 10} makes $limit available
-c, --compactOne line per result
-r, --raw-outputPrint strings without quotes, like jq -r
--jsonPrint a JSON envelope instead of the bare result (see below)
--timeout MSStop an evaluation that runs longer (error D1012)
--max-depth NStop an evaluation that nests deeper (error D1011); the default is 50,000
--max-string-length BYTESStop an evaluation that builds a longer string (error U1001); the default is 256 MiB
--max-array-length NStop an evaluation that builds an array of more items (error U1001); the default is 16,777,216
--max-result-size BYTESStop an evaluation whose result is larger as compact JSON, counting a value each time it appears (error U1001); the default is 256 MiB
--now TIMEThe time seen by $now() and $millis(): milliseconds since the epoch, or an ISO 8601 timestamp such as 2026-01-01T00:00:00Z
--seed NSeed $random() and $shuffle(): the same seed gives the same results

Results are printed the way JavaScript prints JSON, so they match jsonata-js output: 2^64 prints as 18446744073709552000 and 1e21 as 1e+21.

Machine output: --json

With --json, every outcome is one compact JSON object, the envelope, on one line of standard output. Nothing needs to be read from standard error.

OutcomeEnvelope
A result{"ok":true,"result":…}
An undefined result{"ok":true,"undefined":true}
An error{"ok":false,"error":{"code":…,"message":…,"position":…,"line":…,"column":…}}
$ jlens eval --json -i fixtures/online-store-order.json 'customer.name + 1'
{"ok":false,"error":{"code":"T2001","message":"The left side of the \"+\" operator must evaluate to a number","position":15,"line":1,"column":1}}
  • Decide on ok, and match errors by code; message is for people and may change.
  • line and column (1-based, the column in characters) mark the start of the offending text in the expression, as the human report does. position is the jsonata-js position, a UTF-16 offset usually just past the offending token. The three are absent when the error has no location.
  • An input document that is not valid JSON gives an envelope with code U2001, and exit code 2 as without --json.
  • With --filter, a matching document is the result, and a document that does not match prints nothing.
  • With --ndjson, every envelope also carries line, the number of its input line, so that results and errors can be paired with their input. A line that is not valid JSON gives a U2001 envelope, and processing continues.
  • --json cannot be combined with -r, --explain or --audit.
  • Fields are only ever added to the envelope: ignore the ones you do not know.

Many documents: --ndjson

With --ndjson, the input has one JSON document per line, and the expression is evaluated for each one. Results are printed one per line, as they are produced, so arbitrarily large inputs stream through in constant memory.

  • Blank lines are skipped.
  • Lines that are not valid JSON, and evaluations that fail, are reported on standard error with their line number. Processing continues, and the exit code becomes 1.
  • With --json, each result and each error is an envelope that carries its line number, and nothing is written to standard error.
$ jlens eval --ndjson -i fixtures/orders.ndjson '$sum(items.(unitPrice * quantity))'
50000
300
60000
line 5: invalid JSON: expected ident at line 1 column 2
$ jlens eval --ndjson --json -i fixtures/orders.ndjson '$sum(items.(unitPrice * quantity))'
{"ok":true,"line":1,"result":50000}
{"ok":true,"line":2,"result":300}
{"ok":true,"line":4,"result":60000}
{"ok":false,"line":5,"error":{"code":"U2001","message":"The input is not valid JSON: expected ident at line 1 column 2"}}
{"ok":true,"line":6,"undefined":true}

Selecting documents: --filter

With --filter, jlens prints the input documents for which the expression is truthy, instead of the results. Truthiness follows $boolean: false, 0, "", null, empty arrays, empty objects and undefined are false. Combined with --ndjson, this works like a grep for JSON:

$ jlens eval --ndjson --filter -i fixtures/orders.ndjson \
    "customer.login in ['alice.walker99', 'bob.harrison42'] and \$sum(items.(unitPrice * quantity)) > 50000"
{"number":"SO-3","customer":{"login":"bob.harrison42"},"items":[{"type":"toy","unitPrice":60000,"quantity":1}]}

Explaining results

OptionEffect
--explainPrints an explanation of how the result was reached, instead of the result
--audit jsonPrints the full audit tree as JSON (--audit text is the same as --explain)
--audit-level fullRecords every evaluated node, not only decisions
--trace-log FILEWrites every evaluation event to FILE as JSON Lines; combines with the options above
$ jlens eval --no-input --explain '/** @label "Positive" */ 3 > 0 and /** @label "Small" */ 3 < 2'
result: false
✘ Small — 3 < 2 → false (3 < 2)

These options apply to a single document; they cannot be combined with --ndjson or --filter.

jlens check

Parses the expression with error recovery and reports every problem it finds. It warns about calls of functions that are neither built in nor bound in the expression (W0202, with a suggestion such as $summ → $sum), and about calls with a number of arguments the function’s signature does not accept (W0203) or an argument whose type it does not accept (W0205, as in $length(1234)). With --schema, it also reports field names that the schema does not have (see the schema guide), and checks the types of fields passed to functions. Each problem is shown in context:

$ jlens check 'Account.Order[0;].Product'
error[S0202]: Expected "]", got ";"
 --> <expr>:1:16
  |
1 | Account.Order[0;].Product
  |                ^
<expr>: 1 error(s), 0 warning(s)

-f takes several files, and glob patterns such as rules/*.jsonata. Each file is reported in turn, followed by a total. Errors make the exit code 1; warnings alone do not, unless --deny-warnings is given. An unreadable file, or a pattern that matches nothing, ends with exit code 2 before anything is checked.

With --json, the diagnostics of all the files are printed as one compact JSON array on standard output, which suits editors, CI scripts and agents. Each diagnostic has file (absent for an expression given as an argument), the 1-based line and column (counted in characters), then the severity, code, message and span, in byte offsets into the file:

$ jlens check --json 'a +'
[{"line":1,"column":4,"severity":"error","code":"S0207","message":"Unexpected end of expression","span":{"start":3,"end":3}}]

Configuration: jlens.json

check reads the same jlens.json as the language server, so that the command line and the editors report the same problems. It maps file patterns to a schema, the variables and the host functions the expressions are evaluated with: calls of declared host functions are checked like those of built-in ones, and, once variables are declared, references to undeclared ones get W0204. By default, check reads the jlens.json of the current directory, if there is one; --config names another file, whose patterns and paths are relative to its folder.

The configuration applies to files given with -f, by their path. An expression given as an argument matches no pattern, unless --path gives it the path of a file it would be saved as, which need not exist; that suits checking an expression before writing it:

jlens check --path rules/discount.jsonata '$rate(type) * unitPrice'

--schema replaces the schema that the configuration gives a file, and keeps its variables and functions. A configuration that cannot be read, or whose schema cannot be, ends with exit code 2.

To validate a directory of rules in CI, warnings included:

jlens check --deny-warnings -f 'rules/*.jsonata'

jlens test

Runs files of test cases. A case file is a JSON object that gives one expression, inline with expr or as the path of a file with file (relative to the case file), and the cases it is evaluated with:

{
  "file": "../rules/discount.jsonata",
  "bindings": {"discount": 0.1},
  "cases": [
    {"name": "ten percent off", "input": {"items": [{"price": 10}, {"price": 30}]}, "result": 36},
    {"name": "no items", "input": {}, "undefined": true},
    {"name": "text prices", "input": {"items": [{"price": "10"}]}, "error": "T0412"}
  ]
}

Each case has an optional name, the input document (absent for none; null is a document), its own bindings, which are added to those of the file and win over them, and exactly one expectation:

KeyThe case passes when
"result": VALUEthe result equals VALUE. Numbers compare by value (1 equals 1.0), and the keys of objects in any order
"undefined": truethe result is undefined
"error": "CODE"the evaluation, or the compilation, fails with this error code. Messages are not compared

The file may also fix the clock with now (milliseconds or an ISO 8601 timestamp, as eval --now) and the random numbers with seed. Unknown keys are errors, so that a misspelt expectation does not pass unnoticed.

jlens test takes several files, and glob patterns such as tests/*.json. It reports each failed case, with what was expected and what the expression gave, then a summary per file. Had the third case above expected T2001:

$ jlens test tests/discount.json
FAIL tests/discount.json case 3: text prices
  expected: error T2001
  actual:   error T0412: Argument 1 of function "sum" must be an array of "numbers"
tests/discount.json: 2 passed, 1 failed

A failed case makes the exit code 1. A case file that cannot be read, or is invalid, ends with exit code 2 before any case runs. --timeout stops each evaluation after a number of milliseconds (error D1012), and --max-string-length, --max-array-length and --max-result-size cap the size of what it builds, as they do for eval (error U1001).

With --json, the outcome is one compact JSON object on standard output, with the number of cases that passed and failed, and the failures. Each failure has the file, the 1-based case number, the name if there is one, and what was expected and what is actual, both in the shape of the eval --json envelope:

$ jlens test --json tests/discount.json
{"passed":2,"failed":1,"failures":[{"file":"tests/discount.json","case":3,"name":"text prices","expected":{"ok":false,"error":{"code":"T2001"}},"actual":{"ok":false,"error":{"code":"T0412","message":"Argument 1 of function \"sum\" must be an array of \"numbers\"","position":5,"line":1,"column":1}}}]}

jlens functions

Lists the built-in functions by category, with their parameters and a summary. Given a name (with or without $), it describes one function; --json prints the catalog as JSON.

$ jlens functions formatNumber
$formatNumber($value, $picture, $options)
  signature: <n-so?:s>
  category:  Numeric
  Formats a number with an XPath picture string

The function reference is generated from the same catalog.

jlens errors

Lists the error and warning codes by category, with their message templates. Given a code (in any case), it describes one; --json prints the catalog as JSON, each entry with its code, category (static, type, dynamic, guardrail or warning) and template.

$ jlens errors S0202
S0202
  category: Static errors (S)
  message:  Expected {{value}}, got {{token}}

The error reference is generated from the same catalog.

jlens schema

CommandPurpose
jlens schema infer SAMPLES… [--each] [--overrides FILE] [-o SCHEMA]Infer a JSON Schema from sample documents
jlens schema complete <EXPR> --schema SCHEMA [--cursor N] [--json]List completions at a cursor position, given in characters (default: the end)

Both are described in the schema guide.

jlens lsp

Runs the language server on standard input and output. Editors start it; it is configured by the jlens.json of each workspace folder. See the editors guide.

jlens ast

Prints the processed syntax tree as JSON. This is the tree the evaluator walks, with node ids, spans, stages and annotations. Flags show earlier stages of the pipeline:

FlagOutput
(none)Processed tree (Ast)
--rawRaw parse tree, before post-processing
--tokensToken stream, with spans
--recoverKeep going after syntax errors; print the partial tree, with diagnostics on standard error

jlens ast is mostly useful for understanding how an expression is read, for example which part an annotation covers. The parsing chapter explains the tree.