This specification describes the PODPORA:PATCH format and its processing rules. The format is mainly designed for use with the HTTP PATCH method, allowing users to describe a series of changes to the content of a target resource.
It's a direct alternative to JSON Patch and JSON Merge Patch, aimed at reducing storage usage and providing enhanced support for list operations in JSON.
- Abstract
- Table of Contents
- Introduction
- MIME media type
- PATCH Structure
- PATCH Operations for Dictionaries
- PATCH Operations for Lists
The PODPORA:PATCH format represents the changes to be made to a target JSON document in a format similar to the document being modified, providing a flexible and storage-efficient way to modify JSON objects, with enhanced handling for lists through the use of serial keys. Implementations should take care to handle edge cases, such as invalid serial keys and mismatched types, according to the rules described in this document.
Definitions used in this specification:
- JSON: The target data structure, which may be a dictionary or list, that PATCH will modify.
- PATCH: A dictionary-based file format used to describe changes to be applied to a JSON object.
The application/podpora-patch+json
media type allows clients to signal that they want the server to determine
which specific changes should be made to a resource. The server is responsible for assessing whether a change is
appropriate and whether the client is authorized to request it. The process for making these determinations falls
outside the scope of this specification.
Resources that utilize addressed media type must adhere to the "application/json" media type. As a result, they are subject to the same encoding rules and considerations outlined in Section 8 of RFC7159.
Also, all security concerns mentioned in Section 5 of RFC5789 apply when using the HTTP PATCH method with the "application/podpora-patch+json" media type.
- PATCH is always represented as a dictionary.
- Each key in PATCH corresponds to a key in the target JSON object.
- PATCH operations can manipulate both dictionaries and lists within JSON.
If a key in PATCH is named "_"
, it is always ignored unless explicitly handled, as described in later sections.
If the value in PATCH is not an object, the corresponding value in JSON is overwritten:
JSON = {"a": 1}
PATCH = {"a": 6}
Result: JSON = {"a": 6}
JSON = {}
PATCH = {"a": [{"a": 3}, {"a": 4}]}
Result: JSON = {"a": [{"a": 3}, {"a": 4}]}
If the key *
is set to null
, the corresponding key in the target JSON is deleted:
JSON = {"a": 1}
PATCH = {"a": {"*": null}}
Result: JSON = {}
If no *
symbol is present in PATCH, the value is simply overwritten, even if the value is null
.
JSON = {"a": 1}
PATCH = {"a": null}
Result: JSON = {"a": null}
If the key *
is not null
, it represents an overwrite or creation of a new value, based on whether the target exists:
JSON = {"a": 1}
PATCH = {"a": {"*": {"foo": "bar"}}}
Result: JSON = {"a": {"foo": "bar"}}
JSON = {}
PATCH = {"a": {"*": {"foo": "bar"}}}
Result: JSON = {"a": {"foo": "bar"}}
Note: When using *
, additional keys in the PATCH object are ignored:
PATCH = {"a": {"*": 4, "foo": "bar"}}
Equivalent to: {"a": {"*": 4}}
If the value in PATCH is a dictionary, it performs an edit on the target JSON:
JSON = {
"a": 23,
"b": {"c": 123, "d": 432}
}
PATCH = {"b": {"d": 999}}
Result: JSON = {
"a": 23,
"b": {"c": 123, "d": 999}
}
PATCH operations are applied recursively to nested objects:
JSON = {"a": 23}
PATCH = {"a": {"foo": "bar"}}
Error: "Invalid patch, as '23' is not a dictionary or list."
To resolve this error, use *
for overwriting:
PATCH = {"a": {"*": {"foo": "bar"}}}
Other actions like, for example, deletion within nested objects also work recursively:
JSON = {
"a": 23,
"b": {"c": 123, "d": 432}
}
PATCH = {"b": {"d": {"*": null}}}
Result: JSON = {
"a": 23,
"b": {"c": 123}
}
Lists in JSON can be directly modified using PATCH, which replaces or edits list elements:
JSON = {
"a": 23,
"b": [{"foo": "bar"}, {"foo": "bar"}, {"foo": "bar"}]
}
PATCH = {
"b": [{"foo": "bar"}, {"foo": "bar"}]
}
Result: JSON = {
"a": 23,
"b": [{"foo": "bar"}, {"foo": "bar"}]
}
For more complex list manipulations, each item in the list can be assigned a serial key (_
), which is a unique identifier:
JSON = {
"a": 23,
"b": [
{"_": "111111", "foo": "bar"},
{"_": "222222", "foo": "bar"},
{"_": "333333", "foo": "bar"}
]
}
PATCH can modify list items using their serials:
PATCH = {"b": {"222222": {"foo": "baz"}}}
Result: JSON = {
"a": 23,
"b": [
{"_": "111111", "foo": "bar"},
{"_": "222222", "foo": "baz"},
{"_": "333333", "foo": "bar"}
]
}
PATCH can delete list items using their serial:
PATCH = {"b": {"222222": {"*": null}}}
Result: JSON = {
"a": 23,
"b": [
{"_": "111111", "foo": "bar"},
{"_": "333333", "foo": "bar"}
]
}
New list items can be created using the serial key and the *
symbol:
PATCH = {"b": {"999999": {"*": {"foo": "bar"}}}}
Result: JSON = {
"a": 23,
"b": [
{"_": "111111", "foo": "bar"},
{"_": "222222", "foo": "bar"},
{"_": "333333", "foo": "bar"},
{"_": "999999", "foo": "bar"}
]
}
PATCH implementations should either ignore or throw errors for non-existent serials:
PATCH = {"b": {"999999": {"foo": "bar"}}} // Should throw or be ignored
However, using the *
method to create new items is valid:
PATCH = {"b": {"999999": {"*": {"foo": "bar"}}}} // Creates a new item