



= JSON Module

The `json` module registers a `map` function to allow mapping fields from
JSON structures to attributes. It also adds some xlats for processing and
generating JSON documents.

## JSON 'map' function

The path through the JSON document's tree is specified with FR jpath,
which is the FreeRADIUS implementation of the jpath grammar described
at http://goessner.net/articles/JsonPath/

Selectors currently implemented are:

[options="header,autowidth"]
|===
| Selector                 | Description
| $                        | Root node (only valid at the start of the path).
| @                        | Current node (only valid at the start of the path).
| .<name>                  | A field within an object.
| [<idx>]                  | Index within an array.
| [<start>:<end>[:<step>]] | A slice within an array (identical to the Python syntax).
| [<idx>,<start>:<end>]    | Multiple indexes/slices within an array.
| .*                       | All the children of the current node.
| ..                       | Recursive descent.
|===

[NOTE]
====
  * Automatic casting will occur between JSON and attribute types where possible.
  * Assignment of JSON objects/arrays to strings is supported, in which case the
  JSON serialized form of the object/array is used.
  * If a jpath matches multiple nodes, unless the map includes the `+=` operator
  * only the first node's value will be used.
  * If the map uses `+=` then multiple instances of the attribute will be created,
  each holding a different node value.
====


## Sample

Assuming that the URL http://example.org/api/user/$username replies with
some JSON content as below.

[source, json]
----
{
  "user": "bob",
  "account number": 7124503,
  "groups": [
    "admin",
    "networks",
    "bob"
  ]
}
----

In this case, the `rest` module can be used to get the JSON data and the
fields can be accessed using 'map' as shown in the example below.

[source, unlang]
----
map json "%rest(GET http://example.org/api/user/%{User-Name})" {
  &Foo            := '$.account number'
  &Group          += '$.groups.*'
}
----



## Configuration Settings



The only options for the JSON module are to control the output
format of the `json.encode` xlat.



output_mode:: set the format of JSON documenta
that should be created. This may be one of:

- object
- object_simple
- array
- array_of_names
- array_of_values

Examples of each format are given below.



### Formatting options for attribute names


prefix:: Add a colon-delimited prefix to all
attribute names in the output document. For example,
with a prefix of "foo", `link:https://freeradius.org/rfc/rfc2865.html#User-Name[User-Name]` will be output as
`foo:User-Name`.



### Formatting options for attribute values



single_value_as_array:: always put values in an array

Output formats will by default put single values as a
JSON object (string, integer, etc). More than one
value will, depending on the output format, be added
as an array.

When this option is enabled, values will always be
added as an array.



enum_as_integer:: output the integer value of
enumerated attributes

Where an attribute has enum values, the textual
representation of the value will normally be output.
Enable this option to force the numeric value
instead.



always_string:: force all values to be strings

Integer values are normally written to the JSON
document as numbers (i.e. without quotes). Enable
this option to force all values to be as quoted
strings.





## Expansions

rlm_json provides the below xlat functions to handle the JSON documents.

### %json.jpath_validate(...)

Determine if a jpath expression is valid.

NOTE: Validate parser for everything except unions and expressions.

.Return: _size_t_

.Example

[source,unlang]
----
string payload
payload := '$.my.json.payload[1]'
&reply.Reply-Message := "Validation of %{payload} is %json.jpath_validate($.my.json.payload[1])"
----

.Output

```
Validation of $.my.json.payload[1] is 20:$.my.json.payload[1]
```

### %json.quote(...)

Escapes string for use as a JSON string.

.Return: _string_

.Example

[source,unlang]
----
string path
&path := "caipirinha/gelada"
&reply.Reply-Message := "The string %{path} should be %json.quote(%{path}) to be a valid JSON string."
----

.Output

```
The string caipirinha/gelada should be caipirinha\\/gelada to be a valid JSON string.
```

### %json.encode(...)

Generates a JSON document from a given list of attribute templates. The
format of document generated can be controlled with the 'encode' section in
the module configuration. Attribute values will automatically be escaped so
they are JSON-safe.

NOTE: The name of the xlat is based on the instance name of this module. If
the module was defined as `json jdoc {...}`, then the xlat name will be
`jdoc_encode`.

The xlat should be passed a list of attributes to encode. Each attribute
(after template expansion) will be added to a list of attributes to include
in the JSON document. If any of the attributes given are preceded with a `!`
then they are removed from the list. Once all attributes have been processed,
the JSON document will be created using this list.

For example, the following will produce a JSON document with two attributes in
it, `link:https://freeradius.org/rfc/rfc2865.html#User-Name[User-Name]` and `link:https://freeradius.org/rfc/rfc2865.html#Calling-Station-Id[Calling-Station-Id]`, from the RADIUS request:

.Example

```
%json.encode(&User-Name, &Calling-Station-Id)
```

The following will include all attributes in the RADIUS request, except for
`link:https://freeradius.org/rfc/rfc2865.html#User-Password[User-Password]`:

.Example

```
%json.encode(&request[*], !&User-Password)
```

In another (contrived) example, all the attributes in the RADIUS request will
be included in the document, _except_ any attributes in the RADIUS reply.
`&User-Name` will be included from the control list, too, if it exists:

.Example

```
%json.encode(&request[*], !&reply[*], &control.User-Name)
```

#### Output format modes

There are a number of output modes, each generating a different format of
JSON document.

NOTE: In the JSON document, "type" is the type of the _attribute_, which is
not necessarily the same as the type of the "value" in the document. See e.g.
`link:https://freeradius.org/rfc/rfc2865.html#Login-Service[Login-Service]` above, an enumerated value.

The following examples assume the three attributes are being added to the
JSON document:

```
User-Name = bob
Filter-Id = ab
Filter-Id += cd
```

#### Object output mode examples

These modes output a JSON object.

.Output mode "object"

[source,json]
----
{
  "User-Name": {
    "type": "string",
    "value": "bob"
  },
  "Filter-Id": {
    "type": "string",
    "value": ["ab","cd"]
  }
}
----

.Output mode "object_simple"

[source,json]
----
{
  "User-Name": "bob",
  "Filter-Id": ["ab","cd"]
}
----

#### Array output mode examples

The "array" mode is a list of objects, each containing an attribute. If the
"single_value_as_array" value option is set then each attribute will only
appear once in the array, and "value" will be a list of all the values from
the same attribute.

.Output mode "array"

[source,json]
----
[
  {
    "name": "User-Name",
    "type": "string",
    "value": "bob"
  },
  {
    "name": "Filter-Id",
    "type": "string",
    "value": "ab"
  },
  {
    "name": "Filter-Id",
    "type": "string",
    "value": "cd"
  }
]
----

.Output mode "array" when "single_value_as_array" is also set

[source,json]
----
[
  {
    "name": "User-Name",
    "type": "string",
    "value": ["bob"]
  },
  {
    "name": "Filter-Id",
    "type": "string",
    "value": ["ab","cd"]
  }
]
----

The following output modes either do not include the attribute names or
values. They are likely to be useful only when the attributes are
individually specified and _guaranteed to exist_. In this case the attribute
names in `array_of_names` will have corresponding indexes to the values in
`array_of_values`.

.Output mode "array_of_names"

[source,json]
----
[
  "User-Name",
  "Filter-Id",
  "Filter-Id"
]
----

.Output mode "array_of_values"

[source,json]
----
[
  "bob",
  "ab",
  "cd"
]
----


== Default Configuration

```
json {
	encode {
#		output_mode = object
		attribute {
#			prefix =
		}
		value {
#			single_value_as_array = no
#			enum_as_integer = no
#			always_string = no
		}
	}
}
```
