Description
'properties' keyword is defined in chapter 10.3.2.1 of the core specification:
Validation succeeds if, for each name that appears in both the instance and as a name within this keyword's value, the child instance for that name successfully validates against the corresponding schema.
The keyword 'properties' is therefore used to identify child instances to associate them with a subschema. This association is made by matching names.
However, there is a dedicated way to identify a child instance: the JSON Pointer.
We can therefore make this association in a simpler way by indicating in the Schema only the JSON Pointer of the corresponding child instance.
This way of identifying a child instance is more understandable because it clearly separates a name in an instance and a pointer in a schema.
Example:
{
"$id": "https://example.com/complex-object.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Complex Object",
"type": "object",
"properties": {
"name": { "type": "string"},
"age": { "type": "integer", "minimum": 0},
"address": {
"type": "object",
"properties": {
"street": { "type": "string"},
"city": { "type": "string"},
"state": {"type": "string"},
"postalCode": { "type": "string", "pattern": "\\d{5}"}},
"required": ["street", "city", "state", "postalCode"]},
"hobbies": {
"type": "array",
"items": { "type": "string"}}},
"required": ["name", "age"]
}
Example with JSON Pointer:
{
"$id": "https://example.com/complex-object.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Complex Object",
"type": "object",
"/name": {"type": "string"},
"/age": {"type": "integer", "minimum": 0},
"/address": {
"type": "object",
"/street": {"type": "string"},
"/city": {"type": "string"},
"/state": {"type": "string"},
"/postalCode": {"type": "string", "pattern": "\\\\d{5}"},
"required": ["/street", "/city", "/state", "/postalCode"]},
"/hobbies": {
"type": "array",
"items": {"type": "string"}},
"required": ["/name", "/age"]
}
Furthermore, the use of the json-pointer is not limited to json-objects and can be generalized to json-arrays, which is an alternative to 'prefixItems'. It also allows you to associate a subschema only with the targeted json element.
Example:
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "enum": ["Street", "Avenue", "Boulevard"] },
{ "enum": ["NW", "NE", "SW", "SE"] }]
}
Example with JSON Pointer:
{
"type": "array",
"/0": { "type": "number" },
"/1": { "type": "string" },
"/2": { "enum": ["Street", "Avenue", "Boulevard"] },
"/3": { "enum": ["NW", "NE", "SW", "SE"] }
}
Note 1: This principle can also be extended to pointers of rank greater than 1
{"/adress/state": {"type": "string"}
Note 2 : The use of JSON pointer and the keyword 'properties' are compatible if we accept for the keyword 'required' indifferently name or JSON pointer.
Note 3: The Python functions below also show the conversion between a schema with keyword properties and one without.
def add_prop(json_val):
'''add "properties" keyword for JSON Schema using JSON Pointer'''
json_value = copy(json_val)
if isinstance(json_value, list):
return [add_prop(val) for val in json_value]
if isinstance(json_value, dict):
prop = {k[1:]: add_prop(v) for k,v in json_value.items() if k[0] == '/'}
return {k: add_prop(v) for k,v in json_value.items() if k[0] != '/'
} | ({'properties': prop} if prop else prop)
return json_value[1:] if isinstance(json_value, str) and json_value[0] == '/' else json_value
def del_prop(json_val):
'''remove "properties" keyword and add JSON Pointer for JSON Schema using "properties" '''
json_value = copy(json_val)
if isinstance(json_value, list):
return [del_prop(val) for val in json_value]
if isinstance(json_value, dict):
if 'required' in json_value:
json_value['required'] = ['/' + val for val in json_value['required']]
if 'properties' in json_value:
json_value |= {'/' + k: v for k, v in json_value['properties'].items()}
del(json_value['properties'])
return {k: del_prop(v) for k, v in json_value.items()}
return json_value
# example (if 'schema' is a JSON Schema)
schema == add_prop(del_prop(schema))) # is True