Definitions#

Restrictions#

This package reserves JSON and TOML key $ref for references.

Configuration#

A configuration is defined as

The mapping values and array elements are again configuration instances.

Python implementation: rconf.Value.

Pointers#

A JSON or TOML pointer identifies a value within a JSON or TOML document respectively.

JSON pointers

are defined in RFC 6901: JSON Pointer.

TOML pointers

are defined here as TOML keys, with the addition of array indices as defined for JSON pointers [1] .

Python implementation: rconf.pointer.Pointer.

References#

An object or table (mapping) with a $ref key will be substituted by the value its string value references, compatible with the JSON Reference draft.

The reference JSON string or TOML string value is a URL with an optional fragment identifier [2] that follows the representation of the referent document: a JSON or TOML pointer.

Reference resolution is applied depth-first.

Example#

A configuration with references to a TOML document (with TOML pointer) and a JSON document (with JSON pointer):

[pypa_build]
"$ref" = "https://raw.githubusercontent.com/pypa/build/main/pyproject.toml#project.description"

[icon]
"$ref" = "https://github.com/manifest.json#/icons/0"
{
    "pypa_build": {
        "$ref": "https://raw.githubusercontent.com/pypa/build/main/pyproject.toml#project.description"
    },
    "icon": {
        "$ref": "https://github.com/manifest.json#/icons/0"
    }
}
[pypa_build]
$ref = https://raw.githubusercontent.com/pypa/build/main/pyproject.toml#project.description

[icon]
$ref = https://github.com/manifest.json#/icons/0

or more compact with implicitly created TOML tables,

pypa_build."$ref" = "https://raw.githubusercontent.com/pypa/build/main/pyproject.toml#project.description"
icon."$ref"       = "https://github.com/manifest.json#/icons/0"

At the time of writing, each of these results in the following

pypa_build = "A simple, correct Python build frontend"

[icon]
sizes = "114x114"
src = "https://github.githubassets.com/assets/apple-touch-icon-114x114-09ce42d3ca4b.png"
{
    "pypa_build": "A simple, correct Python build frontend",
    "icon": {
        "sizes": "114x114",
        "src": "https://github.githubassets.com/assets/apple-touch-icon-114x114-09ce42d3ca4b.png"
    }
}

Patches#

An object or table with a $ref key can contain additional key/value pairs for patching. In this case, the reference value is copied.

The patch operations used here are defined in RFC 6902: JSON Patch using op, path, value and from fields. Path and from fields use the pointer representation of the document being patched.

Operation assign has been added as a simplified replace, dropping the requirement that the target location must exist for the operation to be successful. It also allows appending to an array with key -.

Operation merge has been added to merge an object into an object or extend an array with an array. When merging objects, existing keys will be replaced.

Three patch notations are allowed:

An array of operation objects

is a $patch key with array value, containing JSON Patch operation objects.

An array of shorthand operation arrays

is a $patch key with array value, containing shorthand operation arrays.

Key/value-pair assignments

are key/value pairs describing path/value assignments [3].

Shorthand operation arrays each consist of

  • a shorthand op (+-@<$?=&),

  • a path and

  • a from or value field if relevant, depending on the operation.

Operation

Shorthand

Third element

add

+

value

remove

-

-

replace

@

value

move

<

from

copy

$

from

test

?

value

assign

=

value

merge

&

value

The $patch array can contain a mix of operation objects and shorthand operation arrays. $patch-array operations are applied in order of appearance, before any key/value-pair assignment.

Failing tests will raise an exception [4].

A patch is applied immediately after dereferencing the corresponding reference.

Example#

[database]
ports        = [8000, 8001, 8002]
data         = [["delta", "phi"], [3.14]]
temp_targets = { cpu = 79.5, case = 72.0 }
enabled      = true

[rfc6902_patched]
"$ref" = "#database"
# RFC 6902 patch array
"$patch" = [
    { op = "assign", path = "data.0", value = "DeltaPhi" },
    { op = "replace", path = "data.1.0", value = 3.14159 },
    { op = "assign", path = "data.1.-", value = "radians" },
    { op = "add", path = "ports.-", value = 8003 },
    { op = "remove", path = "ports.0" },
    { op = "replace", path = "temp_targets.cpu", value = 80.0 },
    { op = "move", path = "target_temp_cpu", from = "temp_targets.cpu" },
    { op = "copy", path = "target_temp_case", from = "temp_targets.case" },
    { op = "test", path = "enabled", value = true },
    { op = "replace", path = "temp_targets.case", value = 75.0 },
    { op = "merge", path = "ports", value = [8004, 8005] },
    { op = "merge", path = "temp_targets", value = { lower = 7.0 } },
    { op = "add", path = "icon", value = { "$ref" = "https://api.github.com/emojis#/floppy_disk" } },
]

[shorthand_patched]
"$ref" = "#database"                                # replace shorthand_patched with reference
# key/value-pair assignment                         # Start patching:
"data.0" = "DeltaPhi"                               # - assign "DeltaPhi"
# $patch array
"$patch" = [
    ["@", "data.1.0", 3.14159],                     # - replace 3.14
    ["=", "data.1.-", "radians"],                   # - assign "radians" at array end
    ["+", "ports.-", 8003],                         # - add 8003 at end
    ["-", "ports.0"],                               # - remove 8000
    ["@", "temp_targets.cpu", 80.0],                # - replace 79.5 before move
    ["<", "target_temp_cpu", "temp_targets.cpu"],   # - move from temp_targets.cpu
    ["$", "target_temp_case", "temp_targets.case"], # - copy from temp_targets.case
    ["?", "enabled", true],                         # - test enabled
    # RFC 6902 notation                             # - replace 72.0 after copy
    { op = "replace", path = "temp_targets.case", value = 75.0 },
    ["&", "ports", [8004, 8005]],                   # - merge ports with [8004, 8005]
    ["&", "temp_targets", { lower = 7.0 }],         # - merge temp_targets with {...}
]
# key/value-pair assignment                         # - assign using reference
icon."$ref" = "https://api.github.com/emojis#/floppy_disk"
{
    "database": {
        "ports": [ 8000, 8001, 8002 ],
        "data": [ [ "delta", "phi" ], [ 3.14 ] ],
        "temp_targets": { "cpu": 79.5, "case": 72.0 },
        "enabled": true
    },
    "rfc6902_patched": {
        "$ref": "#/database",
        "$patch": [
            { "op": "assign", "path": "/data/0", "value": "DeltaPhi" },
            { "op": "replace", "path": "/data/1/0", "value": 3.14159 },
            { "op": "assign", "path": "/data/1/-", "value": "radians" },
            { "op": "add", "path": "/ports/-", "value": 8003 },
            { "op": "remove", "path": "/ports/0" },
            { "op": "replace", "path": "/temp_targets/cpu", "value": 80.0 },
            { "op": "move", "path": "/target_temp_cpu", "from": "/temp_targets/cpu" },
            { "op": "copy", "path": "/target_temp_case", "from": "/temp_targets/case" },
            { "op": "test", "path": "/enabled", "value": true },
            { "op": "replace", "path": "/temp_targets/case", "value": 75.0 },
            { "op": "merge", "path": "/ports", "value": [ 8004, 8005 ] },
            { "op": "merge", "path": "/temp_targets", "value": { "lower": 7.0 } },
            { "op": "add", "path": "/icon", "value": { "$ref": "https://api.github.com/emojis#/floppy_disk" } }
        ]
    },
    "shorthand_patched": {
        "$ref": "#/database",
        "/data/0": "DeltaPhi",
        "$patch": [
            [ "@", "/data/1/0", 3.14159 ],
            [ "=", "/data/1/-", "radians" ],
            [ "+", "/ports/-", 8003 ],
            [ "-", "/ports/0" ],
            [ "@", "/temp_targets/cpu", 80.0 ],
            [ "<", "/target_temp_cpu", "/temp_targets/cpu" ],
            [ "$", "/target_temp_case", "/temp_targets/case" ],
            [ "?", "/enabled", true ],
            { "op": "replace", "path": "/temp_targets/case", "value": 75.0 },
            [ "&", "/ports", [ 8004, 8005 ] ] ,
            [ "&", "/temp_targets", { "lower": 7.0 } ]
        ],
        "/icon": { "$ref": "https://api.github.com/emojis#/floppy_disk" }
    }
}

translates to

[database]
ports        = [8000, 8001, 8002]
data         = [["delta", "phi"], [3.14]]
temp_targets = { cpu = 79.5, case = 72.0 }
enabled      = true

[rfc6902_patched]
ports            = [8001, 8002, 8003, 8004, 8005]     # added 8003; removed 8000; merged with [8004, 8005]
data             = ["DeltaPhi", [3.14159, "radians"]] # assigned "DeltaPhi"; replaced 3.14; assigned "radians"
temp_targets     = { case = 75.0, lower = 7.0 }       # cpu moved away; replaced 72.0; merged with { lower = 7.0 }
enabled          = true                               # tested and passed
target_temp_cpu  = 80.0                               # moved from temp_targets, was replaced before move
target_temp_case = 72.0                               # copied from temp_targets, was replaced after copy
icon             = "https://github.githubassets.com/images/icons/emoji/unicode/1f4be.png?v8"

[shorthand_patched]
# Identical to [patched]
# ...
Full translation
[database]
ports = [
    8000,
    8001,
    8002,
]
data = [
    [
        "delta",
        "phi",
    ],
    [
        3.14,
    ],
]
enabled = true

[database.temp_targets]
cpu = 79.5
case = 72.0

[rfc6902_patched]
ports = [
    8001,
    8002,
    8003,
    8004,
    8005,
]
data = [
    "DeltaPhi",
    [
        3.14159,
        "radians",
    ],
]
enabled = true
target_temp_cpu = 80.0
target_temp_case = 72.0
icon = "https://github.githubassets.com/images/icons/emoji/unicode/1f4be.png?v8"

[rfc6902_patched.temp_targets]
case = 75.0
lower = 7.0

[shorthand_patched]
ports = [
    8001,
    8002,
    8003,
    8004,
    8005,
]
data = [
    "DeltaPhi",
    [
        3.14159,
        "radians",
    ],
]
enabled = true
target_temp_cpu = 80.0
target_temp_case = 72.0
icon = "https://github.githubassets.com/images/icons/emoji/unicode/1f4be.png?v8"

[shorthand_patched.temp_targets]
case = 75.0
lower = 7.0
{
    "database": {
        "ports": [
            8000,
            8001,
            8002
        ],
        "data": [
            [
                "delta",
                "phi"
            ],
            [
                3.14
            ]
        ],
        "temp_targets": {
            "cpu": 79.5,
            "case": 72.0
        },
        "enabled": true
    },
    "rfc6902_patched": {
        "ports": [
            8001,
            8002,
            8003,
            8004,
            8005
        ],
        "data": [
            "DeltaPhi",
            [
                3.14159,
                "radians"
            ]
        ],
        "temp_targets": {
            "case": 75.0,
            "lower": 7.0
        },
        "enabled": true,
        "target_temp_cpu": 80.0,
        "target_temp_case": 72.0,
        "icon": "https://github.githubassets.com/images/icons/emoji/unicode/1f4be.png?v8"
    },
    "shorthand_patched": {
        "ports": [
            8001,
            8002,
            8003,
            8004,
            8005
        ],
        "data": [
            "DeltaPhi",
            [
                3.14159,
                "radians"
            ]
        ],
        "temp_targets": {
            "case": 75.0,
            "lower": 7.0
        },
        "enabled": true,
        "target_temp_cpu": 80.0,
        "target_temp_case": 72.0,
        "icon": "https://github.githubassets.com/images/icons/emoji/unicode/1f4be.png?v8"
    }
}

Remarks#

Definition implications#

  • Circular references are allowed, but references cannot point to themselves.

  • Because references are resolved, and patches applied, depth-first, a $ref value can never be patched.

  • Key/value-pair assignments may be applied out of order [5], so they shouldn’t be relied upon if patch order is of importance.

  • Key/value-pair assignment allows only one replacement per path [6].

  • A copy.deepcopy() is applied for reference substitution with patches.