Radiator Server Documentation — v10.34.2

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.

QuerySelects
$.nameThe 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')].nameNames 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.identity can 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.identity to 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.