JSONPath
JSONPath
JSONPath selects values from a JSON document. Use the jsonpath filter in
RadConf expressions to read fields, select array elements, and find matching
values.
Using the jsonpath filter
The filter queries the value on its left:
vars.user_data = """
{
"name": "alice",
"groups": ["admin", "staff"]
}
""";
vars.user_name = vars.user_data | jsonpath("$.name");
vars.user_name receives "alice".
Query syntax and results
$ refers to the root of the value being queried. @ refers to the current
value inside a filter comparison.
| Query | Selects |
|---|---|
$.name | The name field |
$['display-name'] | A field whose name contains a hyphen |
$.groups[0] | The first group |
$.groups[*] | Every group as a separate value |
$.devices[?(@.type == 'Cisco')].name | Names of Cisco routers |
A query can return zero, one, or several values. With the document above,
$.groups[*] returns "admin" and "staff" as separate RadConf values.
$.groups returns the JSON array as one value. Use [*] when assigning array
elements to a multivalue field such as user.group.
The jsonpath filter returns no values when nothing matches. It does not
reject a pipeline by itself. The action that uses the result determines what
happens next.
The JSON file backend also has a jsonpath statement. It selects backend
results from the loaded document; it does not take a value on its left. See
Selecting JSON file results
for how those results control mappings and @each or @once pipelines.
RadConf context references
Prefix a RadConf execution-context accessor
with $ to use its value in a query. References read the values available at
the query's evaluation point, including pipeline variables, backend arguments,
and protocol attributes.
For example, select groups from an object keyed by identity:
vars.accounts = """
{
"users": {
"alice": { "groups": ["admin", "staff"] },
"bob": { "groups": ["staff"] }
}
}
""";
vars.identity = "alice";
user.group = vars.accounts | jsonpath("$.users[$vars.identity].groups[*]");
This selects Alice's groups without searching through every user entry.
In a request pipeline, $aaa.identity can select the requesting user instead
of $vars.identity.
Use references without quotes in object keys, array indexes, and comparisons. For example, select enabled routers of a particular type:
vars.inventory = """
{
"devices": [
{ "name": "router1", "type": "Cisco", "enabled": true },
{ "name": "router2", "type": "MikroTik", "enabled": true },
{ "name": "router3", "type": "Cisco", "enabled": false }
]
}
""";
vars.router_type = "Cisco";
vars.enabled = true;
vars.device_names = vars.inventory | jsonpath("$.devices[?(@.type == $vars.router_type && @.enabled == $vars.enabled)].name");
The result is "router1": it is a Cisco router and is enabled. && requires
both comparisons to match. $vars.enabled remains a JSON boolean; it is not
converted to the string "true".
String references are quoted and escaped automatically. Apostrophes,
backslashes, and control characters remain part of the value. References cannot
change the query structure.
References inside JSONPath quoted strings remain literal.
$.users['$aaa.identity'] selects a key named $aaa.identity, rather than
reading the identity.
Reusing a JSON document
The json filter converts a JSON string into a parsed JSON value. This step is
optional: jsonpath accepts both forms and parses strings automatically.
For a large document that you access many times, convert it once with json
and store the result in a pipeline variable. This avoids parsing the string
again for each query:
vars.doc = vars.user_data | json;
vars.user_name = vars.doc | jsonpath("$.name");
user.group = vars.doc | jsonpath("$.groups[*]");
vars.doc is a variable you create.
Example JSON sources
The following examples show how to use the jsonpath filter with data available in different
contexts.
JSON file backend results
In JSON file query mappings and result pipelines, doc contains the full
loaded document and item contains the current selected result. The root $
is relative to whichever value you pass to the filter. Querying item reads
from that result; querying doc reads from the full document.
See JSON file backend mappings for examples of mapping result fields.
RadiatorDB documents
In RadiatorDB query mappings
and result pipelines, doc is the current document returned by the query.
For a document containing "groups": ["admin", "staff"], read its groups with:
mapping {
user.group = doc | jsonpath("$.groups[*]");
}
RadiatorDB selects documents using its query configuration. The jsonpath
filter extracts values from the selected document; it does not select database
records.
SQL query mappings
Inside an SQL query mapping, column names supply the values. This PostgreSQL
query reads JSON from a profile column:
query "FIND_USER" {
statement "SELECT profile FROM users WHERE username = $1";
bindings {
aaa.identity;
}
mapping {
user.group = profile | jsonpath("$.groups[*]");
}
}
The filter accepts both native JSON values and JSON stored as text. SQL bindings select the database row; JSONPath queries the returned column value. See the PostgreSQL backend.
For a nullable column, supply an empty object before querying:
mapping {
user.group = profile | default("{}") | jsonpath("$.groups[*]");
}
A NULL column or an object without groups then produces no group values.
HTTP request bodies
An HTTP request with an application/json body exposes the parsed document
through http.request. For a body containing "name": "alice", read the name
in the request pipeline:
vars.user_name = http.request | jsonpath("$.name");
See HTTP request bodies for body parsing and availability.
Migrating interpolated queries
Replace %{...} interpolation inside JSONPath expressions with unquoted
$accessor references. For example, replace
$.users['%{aaa.identity}'] with $.users[$aaa.identity].
[!WARNING]
aaa.identitycan contain unauthenticated, client-controlled input. Interpolating it into a JSONPath expression enables JSONPath injection: quotes or backslashes can change the query and select another user's record, including password or group fields. Use$aaa.identityto insert the identity as a literal value with automatic quoting and escaping.
Missing references and query errors
If any context reference is absent, the whole query returns no matches. The
reference is not replaced with an empty string or JSON null, even in !=
comparisons or queries containing alternative conditions.
Radiator logs JSONPath context reference is missing, which identifies the absent accessor. Check where that value should be set if an empty result is unexpected.
A filter with no matching values does not reject the pipeline. A JSON file
backend jsonpath statement with no matches rejects the backend action. See
JSON file backend rejection.
References must supply one literal value. Multiple values, maps, masked passwords, and byte strings that are not valid UTF-8 produce errors. Invalid static JSONPath queries prevent the configuration from loading.
During execution, invalid JSON input or query errors produce a filter error
value. A query error in a backend jsonpath statement fails the backend
action. These errors differ from an ordinary empty result.
Use recover to handle a filter error:
vars.user_name = vars.user_data | jsonpath("$.name") | recover("unknown");
This fallback applies to errors, such as invalid JSON input. To handle a
missing field or an empty result, use default instead:
vars.user_name = vars.user_data | jsonpath("$.name") | default("unknown");
See Filter error handling for how error values affect assignments and pipeline execution.