# More JSON Configuration!
[](https://pypi.org/project/mo-json-config/)
[](https://github.com/klahnakoski/mo-json-config/actions/workflows/build.yml)
[](https://coveralls.io/github/klahnakoski/mo-json-config?branch=dev)
A JSON template format intended for configuration files.
## Motivation
This module reads JSON files and expands references found within. It is much like the IETF's [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03) specification, but with the following differences:
1. This module uses the dot (`.`) as a path separator in the URL fragment. For example, an absolute reference looks like `{"$ref": "#message.type.name"}`, and a relative reference looks like `{"$ref": "#..type.name"}`. This syntax better matches that used by Javascript.
2. The properties found in a `$ref` object are not ignored. Rather, they are to *override* the referenced object properties. This allows you to reference a default document, and replace the particular properties as needed. *more below*
3. References can accept URL parameters: JSON is treated like a string template for more sophisticated value replacement. *see below*
4. You can reference
* http URLs
* files
* environment variables
* keyring values
* AWS SSM parameters
## Quick guide
Load your configuration file:
```python
import mo_json_config
config = mo_json_config.get("file://my_config.json")
```
You may use the global `configuration` object for case-insensitive lookup:
```python
from mo_json_config import configuration
# use |= operator to add more configuration
configuration |= {"some":{"deep":"value"}}
assert configuration.someDeep == "value"
```
## Schemes
This module can load configuration from a number of sources, and you can access them via URI scheme. Beyond the common `file` and `https` schemes, there are
#### Environment Variables
Use the `env` scheme for accessing environment variables:
{
"host": "mail.example.com",
"username": "ekyle",
"password": {"$ref": "env://MAIL_PASSWORD"}
}
#### Keystore Values
The [keyring](https://pypi.org/project/keyring/) library can be used with the `keyring` scheme:
{
"host": "mail.example.com",
"username": "ekyle",
"password": {"$ref": "keyring://ekyle@mail.example.com"}
}
The host is in `<username>@<server_name>` format; invoking `keyring.get_password(server_name, username)`. You may also set the username as a parameter:
{
"host": "mail.example.com",
"username": "ekyle",
"password": {"$ref": "keyring://mail.example.com?username=ekyle"}
}
> Be sure to `pip install keyring` to use keyring
#### AWS SSM
The `ssm` scheme can be used to read from the AWS parameter store. Here is an example that will read all parameters that start with "/configuration" and adds them to the global configuration object:
```python
from mo_json_config import get, configuration
configuration |= get("ssm:///configuration")
```
## String Template Expansion
Before going into the minutia of expanding `$ref` objects, let's look at simpler string template expansion: Any string that contains a reference (of the form `{scheme://...}`) will have that reference replaced with the string it points to.
Consider an example where we want to name a number of components with a common application name. You may define them by using.
{
"database_name": "{env://APP_NAME}-database"
"queue_name": "{env://APP_NAME}-queue"
}
If we assume
```
export APP_NAME=my-app-name
```
then the above JSON will expand to
{
"database_name": "my-app-name-database"
"queue_name": "my-app-name-queue"
}
These references can be used in the `$ref` object, as well, providing another level of indirection to the configuration file.
## Using references in config files
The `$ref` property is special. Its value is interpreted as a URL pointing to more JSON
### Absolute Internal Reference
The simplest form of URL is an absolute reference to a node in the same
document:
{
"message": "Hello world",
"repeat": {"$ref": "#message"}
}
The reference must start with `#`, and the object with the `$ref` is replaced
with the value it points to:
{
"message": "Hello world",
"repeat": "Hello world"
}
### Relative Internal References
References that start with dot (`.`) are relative, with each additional dot
referring to successive parents. In this case the `..` refers to the
ref-object's parent, and expands just like the previous example:
{
"message": "Hello world",
"repeat": {"$ref": "#..message"}
}
### File References
Configuration is often stored on the local file system. You can in-line the
JSON found in a file by using the `file://` scheme:
It is good practice to store sensitive data in a secure place...
{# LOCATED IN C:\users\kyle\password.json
"host": "database.example.com",
"username": "kyle",
"password": "pass123"
}
...and then refer to it in your configuration file:
{
"host": "example.com",
"port": "8080",
"$ref": "file:///C:/users/kyle/password.json"
}
which will be expanded at run-time to:
{
"host": "example.com",
"port": "8080",
"username": "kyle",
"password": "pass123"
}
Please notice the triple slash (`///`) is referring to an absolute file
reference.
### References To Objects
Ref-objects that point to other objects (dicts) are not replaced completely,
but rather are merged with the target; with the ref-object
properties taking precedence. This is seen in the example above: The "host"
property is not overwritten by the target's.
### Relative File Reference
Here is the same, using a relative file reference; which is relative to the
file that contains this JSON
{#LOCATED IN C:\users\kyle\dev-debug.json
"host": "example.com",
"port": "8080",
"$ref": "file://password.json"
}
### Home Directory Reference
You may also use the tilde (`~`) to refer to the current user's home directory.
Here is the same again, but this example can be anywhere in the file system.
{
"host": "example.com",
"port": "8080",
"$ref": "file://~/password.json"
}
### HTTP Reference
Configuration can be stored remotely, especially in the case of larger
configurations which are too unwieldy to inline:
{
"schema":{"$ref": "https://example.com/sources/my_db.json"}
}
### Scheme-Relative Reference
You are also able to leave the scheme off, so that whole constellations of
configuration files can refer to each other no matter if they are on the local
file system, or remote:
{# LOCATED AT SOMEWHERE AT http://example.com
"schema":{"$ref": "///sources/my_db.json"}
}
And, of course, relative references are also allowed:
{# LOCATED AT http://example.com/sources/dev-debug.json
"schema":{"$ref": "//sources/my_db.json"}
}
### Fragment Reference
Some remote configuration files are quite large...
{# LOCATED IN C:\users\kyle\password.json
"database":{
"username": "kyle",
"password": "pass123"
},
"email":{
"username": "ekyle",
"password": "pass123"
}
}
... and you only need one fragment. For this use the hash (`#`) followed by
the dot-delimited path into the document:
{
"host": "mail.example.com",
"username": "ekyle",
"password": {"$ref": "//~/password.json#email.password"}
}
### Parameters Reference
You can reference the variables found in `$ref` URL by using the `param` scheme. For example, the following JSON document demands that it be provided with a `password` parameter:
{ # LOCATED AT http://example.com/machine_config.json
"host": "mail.example.com",
"username": "ekyle",
"password": {"$ref": "param:///password"}
}
> The `param` scheme only accepts dot-delimited paths.
The above parametric JSON can be expanded with a $ref
{"config": {
"$ref": "http://example.com/machine_config.json?password=pass123"
}}
expands to
{"config": {
"host": "mail.example.com",
"username": "ekyle",
"password": "pass123"
}}
URL parameters and `$ref` properties can conflict. Let's consider
{"config": {
"$ref": "http://example.com/machine_config.json?password=pass123",
"password": "123456"
}}
the URL paramters are used to expand the given document, **then** the `$ref` properties override the contents of the document:
{"config": {
"host": "mail.example.com",
"username": "ekyle",
"password": "123456"
}}
## Comments
End-of-line Comments are allowed, using either `#` or `//` prefix:
```
"key1": "value1", //Comment 1
```
```
"key1": "value1", # Comment 1
```
Multiline comments are also allowed, using either Python's triple-quotes
(`""" ... """`) or Javascript's block quotes `/*...*/`
```
{
"key1": /* Comment 1 */ "value1",
}
```
```
"key1": """Comment 1""" "value1",
```
## Parameterized JSON
The `param` scheme is a good way to set property values in a document, but sometimes that is not enough. Sometimes you want to parameterize property names, or change the document structure in unconventional ways. For these cases, JSON documents are allowed named parameters at the unicode level. Parameters are surrounded by moustaches `{{.}}`:
```javascript
{//above_example.json
{var_name}: "value"
}
```
Parameter replacement is performed on the unicode text before being interpreted by the JSON parser. It is your responsibility to ensure the parameter replacement will result in valid JSON.
You pass the parameters by including them as URL parameters:
{"$ref": "//~/above_example.json?var_name=%22hello%22"}
Which will expand to
{
"hello": "value"
}
The pipe (`|`) symbol can be used to perform some common conversions
{
{{var_name|quote}}: "value"
}
The `quote` transformation will deal with quoting, so ...
{"$ref": "//~/above_example.json?var_name=hello"}
... expands to the same:
{
"hello": "value"
}
Please see [`expand_template()` in the `strings` module](https://github.com/klahnakoski/mo-logs/blob/dev/mo_logs/strings.py) for more on the parameter replacement, and transformations available
Raw data
{
"_id": null,
"home_page": "https://github.com/klahnakoski/mo-json-config",
"name": "mo-json-config",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Kyle Lahnakoski",
"author_email": "kyle@lahnakoski.com",
"download_url": "https://files.pythonhosted.org/packages/3b/bb/308f3565240858fdfa453b5f45ac624b5f011959834a0ed2c8b5739bfed0/mo_json_config-4.682.25104.tar.gz",
"platform": null,
"description": "# More JSON Configuration!\r\n\r\n[](https://pypi.org/project/mo-json-config/)\r\n[](https://github.com/klahnakoski/mo-json-config/actions/workflows/build.yml)\r\n[](https://coveralls.io/github/klahnakoski/mo-json-config?branch=dev)\r\n\r\nA JSON template format intended for configuration files.\r\n\r\n## Motivation\r\n\r\nThis module reads JSON files and expands references found within. It is much like the IETF's [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03) specification, but with the following differences:\r\n\r\n1. This module uses the dot (`.`) as a path separator in the URL fragment. For example, an absolute reference looks like `{\"$ref\": \"#message.type.name\"}`, and a relative reference looks like `{\"$ref\": \"#..type.name\"}`. This syntax better matches that used by Javascript.\r\n2. The properties found in a `$ref` object are not ignored. Rather, they are to *override* the referenced object properties. This allows you to reference a default document, and replace the particular properties as needed. *more below*\r\n3. References can accept URL parameters: JSON is treated like a string template for more sophisticated value replacement. *see below*\r\n4. You can reference \r\n * http URLs\r\n * files\r\n * environment variables\r\n * keyring values\r\n * AWS SSM parameters\r\n\r\n## Quick guide\r\n\r\nLoad your configuration file:\r\n\r\n```python\r\nimport mo_json_config\r\n\r\nconfig = mo_json_config.get(\"file://my_config.json\")\r\n```\r\n\r\nYou may use the global `configuration` object for case-insensitive lookup:\r\n\r\n\r\n```python\r\nfrom mo_json_config import configuration\r\n\r\n# use |= operator to add more configuration\r\nconfiguration |= {\"some\":{\"deep\":\"value\"}}\r\n\r\nassert configuration.someDeep == \"value\"\r\n\r\n```\r\n\r\n\r\n## Schemes\r\n\r\nThis module can load configuration from a number of sources, and you can access them via URI scheme. Beyond the common `file` and `https` schemes, there are\r\n\r\n\r\n#### Environment Variables\r\n\r\nUse the `env` scheme for accessing environment variables:\r\n\r\n {\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": {\"$ref\": \"env://MAIL_PASSWORD\"}\r\n }\r\n\r\n\r\n#### Keystore Values\r\n\r\nThe [keyring](https://pypi.org/project/keyring/) library can be used with the `keyring` scheme:\r\n \r\n {\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": {\"$ref\": \"keyring://ekyle@mail.example.com\"}\r\n }\r\n\r\nThe host is in `<username>@<server_name>` format; invoking `keyring.get_password(server_name, username)`. You may also set the username as a parameter:\r\n\r\n\r\n {\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": {\"$ref\": \"keyring://mail.example.com?username=ekyle\"}\r\n }\r\n\r\n> Be sure to `pip install keyring` to use keyring\r\n\r\n\r\n#### AWS SSM\r\n\r\nThe `ssm` scheme can be used to read from the AWS parameter store. Here is an example that will read all parameters that start with \"/configuration\" and adds them to the global configuration object:\r\n\r\n```python\r\nfrom mo_json_config import get, configuration\r\n\r\nconfiguration |= get(\"ssm:///configuration\")\r\n```\r\n \r\n## String Template Expansion\r\n\r\nBefore going into the minutia of expanding `$ref` objects, let's look at simpler string template expansion: Any string that contains a reference (of the form `{scheme://...}`) will have that reference replaced with the string it points to. \r\n\r\nConsider an example where we want to name a number of components with a common application name. You may define them by using. \r\n\r\n {\r\n \"database_name\": \"{env://APP_NAME}-database\"\r\n \"queue_name\": \"{env://APP_NAME}-queue\"\r\n }\r\n\r\nIf we assume\r\n\r\n```\r\nexport APP_NAME=my-app-name\r\n```\r\n\r\nthen the above JSON will expand to\r\n\r\n\r\n {\r\n \"database_name\": \"my-app-name-database\"\r\n \"queue_name\": \"my-app-name-queue\"\r\n }\r\n\r\nThese references can be used in the `$ref` object, as well, providing another level of indirection to the configuration file.\r\n\r\n\r\n## Using references in config files\r\n\r\nThe `$ref` property is special. Its value is interpreted as a URL pointing to more JSON\r\n\r\n\r\n### Absolute Internal Reference\r\n\r\nThe simplest form of URL is an absolute reference to a node in the same\r\ndocument:\r\n\r\n\r\n {\r\n \"message\": \"Hello world\",\r\n \"repeat\": {\"$ref\": \"#message\"}\r\n }\r\n\r\n\r\nThe reference must start with `#`, and the object with the `$ref` is replaced\r\nwith the value it points to:\r\n\r\n\r\n {\r\n \"message\": \"Hello world\",\r\n \"repeat\": \"Hello world\"\r\n }\r\n\r\n\r\n### Relative Internal References\r\n\r\nReferences that start with dot (`.`) are relative, with each additional dot\r\nreferring to successive parents. In this case the `..` refers to the\r\nref-object's parent, and expands just like the previous example:\r\n\r\n\r\n {\r\n \"message\": \"Hello world\",\r\n \"repeat\": {\"$ref\": \"#..message\"}\r\n }\r\n\r\n\r\n### File References\r\n\r\nConfiguration is often stored on the local file system. You can in-line the\r\nJSON found in a file by using the `file://` scheme:\r\n\r\nIt is good practice to store sensitive data in a secure place...\r\n\r\n\r\n {# LOCATED IN C:\\users\\kyle\\password.json\r\n \"host\": \"database.example.com\",\r\n \"username\": \"kyle\",\r\n \"password\": \"pass123\"\r\n }\r\n\r\n...and then refer to it in your configuration file:\r\n\r\n\r\n {\r\n \"host\": \"example.com\",\r\n \"port\": \"8080\",\r\n \"$ref\": \"file:///C:/users/kyle/password.json\"\r\n }\r\n\r\n\r\nwhich will be expanded at run-time to:\r\n\r\n\r\n {\r\n \"host\": \"example.com\",\r\n \"port\": \"8080\",\r\n \"username\": \"kyle\",\r\n \"password\": \"pass123\"\r\n }\r\n\r\n\r\nPlease notice the triple slash (`///`) is referring to an absolute file\r\nreference.\r\n\r\n### References To Objects\r\n\r\nRef-objects that point to other objects (dicts) are not replaced completely,\r\nbut rather are merged with the target; with the ref-object\r\nproperties taking precedence. This is seen in the example above: The \"host\"\r\nproperty is not overwritten by the target's.\r\n\r\n### Relative File Reference\r\n\r\nHere is the same, using a relative file reference; which is relative to the\r\nfile that contains this JSON\r\n\r\n\r\n {#LOCATED IN C:\\users\\kyle\\dev-debug.json\r\n \"host\": \"example.com\",\r\n \"port\": \"8080\",\r\n \"$ref\": \"file://password.json\"\r\n }\r\n \r\n\r\n### Home Directory Reference\r\n\r\nYou may also use the tilde (`~`) to refer to the current user's home directory.\r\nHere is the same again, but this example can be anywhere in the file system.\r\n\r\n\r\n {\r\n \"host\": \"example.com\",\r\n \"port\": \"8080\",\r\n \"$ref\": \"file://~/password.json\"\r\n }\r\n\r\n\r\n### HTTP Reference\r\n\r\nConfiguration can be stored remotely, especially in the case of larger\r\nconfigurations which are too unwieldy to inline:\r\n\r\n\r\n {\r\n \"schema\":{\"$ref\": \"https://example.com/sources/my_db.json\"}\r\n }\r\n\r\n\r\n### Scheme-Relative Reference\r\n\r\nYou are also able to leave the scheme off, so that whole constellations of\r\nconfiguration files can refer to each other no matter if they are on the local\r\nfile system, or remote:\r\n\r\n\r\n {# LOCATED AT SOMEWHERE AT http://example.com\r\n \"schema\":{\"$ref\": \"///sources/my_db.json\"}\r\n }\r\n\r\n\r\nAnd, of course, relative references are also allowed:\r\n\r\n\r\n {# LOCATED AT http://example.com/sources/dev-debug.json\r\n \"schema\":{\"$ref\": \"//sources/my_db.json\"}\r\n }\r\n\r\n\r\n### Fragment Reference\r\n\r\nSome remote configuration files are quite large...\r\n\r\n {# LOCATED IN C:\\users\\kyle\\password.json\r\n \"database\":{\r\n \"username\": \"kyle\",\r\n \"password\": \"pass123\"\r\n },\r\n \"email\":{\r\n \"username\": \"ekyle\",\r\n \"password\": \"pass123\"\r\n }\r\n }\r\n\r\n... and you only need one fragment. For this use the hash (`#`) followed by\r\nthe dot-delimited path into the document:\r\n\r\n {\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": {\"$ref\": \"//~/password.json#email.password\"}\r\n }\r\n\r\n### Parameters Reference\r\n\r\nYou can reference the variables found in `$ref` URL by using the `param` scheme. For example, the following JSON document demands that it be provided with a `password` parameter: \r\n\r\n { # LOCATED AT http://example.com/machine_config.json\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": {\"$ref\": \"param:///password\"}\r\n }\r\n\r\n> The `param` scheme only accepts dot-delimited paths.\r\n\r\nThe above parametric JSON can be expanded with a $ref\r\n\r\n\t{\"config\": {\r\n\t\t\"$ref\": \"http://example.com/machine_config.json?password=pass123\"\r\n\t}}\r\n\r\nexpands to \r\n\r\n {\"config\": {\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": \"pass123\"\r\n }}\r\n\r\nURL parameters and `$ref` properties can conflict. Let's consider \r\n\r\n\t{\"config\": {\r\n\t\t\"$ref\": \"http://example.com/machine_config.json?password=pass123\",\r\n\t\t\"password\": \"123456\"\r\n\t}}\r\n\r\nthe URL paramters are used to expand the given document, **then** the `$ref` properties override the contents of the document:\r\n\r\n {\"config\": {\r\n \"host\": \"mail.example.com\",\r\n \"username\": \"ekyle\",\r\n \"password\": \"123456\"\r\n }}\r\n\r\n\r\n## Comments\r\n\r\nEnd-of-line Comments are allowed, using either `#` or `//` prefix:\r\n\r\n```\r\n \"key1\": \"value1\", //Comment 1\r\n```\r\n\r\n```\r\n \"key1\": \"value1\", # Comment 1\r\n```\r\n\r\nMultiline comments are also allowed, using either Python's triple-quotes\r\n(`\"\"\" ... \"\"\"`) or Javascript's block quotes `/*...*/`\r\n\r\n```\r\n{\r\n \"key1\": /* Comment 1 */ \"value1\",\r\n}\r\n```\r\n\r\n```\r\n \"key1\": \"\"\"Comment 1\"\"\" \"value1\",\r\n```\r\n\r\n\r\n## Parameterized JSON\r\n\r\nThe `param` scheme is a good way to set property values in a document, but sometimes that is not enough. Sometimes you want to parameterize property names, or change the document structure in unconventional ways. For these cases, JSON documents are allowed named parameters at the unicode level. Parameters are surrounded by moustaches `{{.}}`:\r\n\r\n```javascript\r\n{//above_example.json\r\n {var_name}: \"value\"\r\n}\r\n```\r\n\r\nParameter replacement is performed on the unicode text before being interpreted by the JSON parser. It is your responsibility to ensure the parameter replacement will result in valid JSON.\r\n\r\nYou pass the parameters by including them as URL parameters:\r\n\r\n\t{\"$ref\": \"//~/above_example.json?var_name=%22hello%22\"}\r\n\r\nWhich will expand to\r\n\r\n {\r\n \"hello\": \"value\"\r\n }\r\n\r\nThe pipe (`|`) symbol can be used to perform some common conversions\r\n\r\n\r\n {\r\n {{var_name|quote}}: \"value\"\r\n }\r\n\r\nThe `quote` transformation will deal with quoting, so ...\r\n\r\n\t{\"$ref\": \"//~/above_example.json?var_name=hello\"}\r\n\r\n... expands to the same:\r\n\r\n {\r\n \"hello\": \"value\"\r\n }\r\n\r\nPlease see [`expand_template()` in the `strings` module](https://github.com/klahnakoski/mo-logs/blob/dev/mo_logs/strings.py) for more on the parameter replacement, and transformations available\r\n\r\n",
"bugtrack_url": null,
"license": "MPL 2.0",
"summary": "More JSON Configuration! JSON configuration files with `$ref` and template overlays",
"version": "4.682.25104",
"project_urls": {
"Homepage": "https://github.com/klahnakoski/mo-json-config"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "61d680475f347b1c00b57665d8991779dc2822b958f1a03f78dd740bc9aad0f1",
"md5": "da6b0134a22515632d6a856f4fd16048",
"sha256": "885a4cabb749ee8bcfcadff11765b167ac2136d18a04fe9ed96c131564f3247f"
},
"downloads": -1,
"filename": "mo_json_config-4.682.25104-py3-none-any.whl",
"has_sig": false,
"md5_digest": "da6b0134a22515632d6a856f4fd16048",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 18829,
"upload_time": "2025-04-15T00:09:55",
"upload_time_iso_8601": "2025-04-15T00:09:55.580636Z",
"url": "https://files.pythonhosted.org/packages/61/d6/80475f347b1c00b57665d8991779dc2822b958f1a03f78dd740bc9aad0f1/mo_json_config-4.682.25104-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "3bbb308f3565240858fdfa453b5f45ac624b5f011959834a0ed2c8b5739bfed0",
"md5": "30d960213481e3422e61d05f309fa5fc",
"sha256": "d082e6e37b2e9ffe5c815ef5ac4ccaf26310eac4c821984fb0144ef504c97784"
},
"downloads": -1,
"filename": "mo_json_config-4.682.25104.tar.gz",
"has_sig": false,
"md5_digest": "30d960213481e3422e61d05f309fa5fc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 22153,
"upload_time": "2025-04-15T00:09:57",
"upload_time_iso_8601": "2025-04-15T00:09:57.278069Z",
"url": "https://files.pythonhosted.org/packages/3b/bb/308f3565240858fdfa453b5f45ac624b5f011959834a0ed2c8b5739bfed0/mo_json_config-4.682.25104.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-04-15 00:09:57",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "klahnakoski",
"github_project": "mo-json-config",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "mo-json-config"
}