Pointers
Schema referencing
A JSON pointer is a way of traversing a JSON document by specifying the path to the desired value. Opis JSON Schema supports both absolute and relative JSON schema pointers.
Absolute pointers
Absolute JSON pointers are used to search for values starting at the root of the document.
That’s why absolute JSON pointers always start with /
(slash). The property names (keys) used to descend
into children are also separated by /
(slash).
If a property name contains
~
, then inside the pointer it must be escaped using ~0
, or if contains /
then
it must be escaped using ~1
.
Here are some examples about how absolute JSON pointers work:
{
"name": "some product",
"price": 10.5,
"features": [
"easy to use",
{
"name": "environment friendly",
"url": "http://example.com"
}
],
"info": {
"onStock": true
},
"a/b": "a"
}
Pointer | Value |
---|---|
/ |
the document itself (root) |
/name |
"some product" |
/price |
10.5 |
/features/0 |
"easy to use" |
/features/1/url |
"http://example.com" |
/info |
{"onStock": true} |
/info/onStock |
true |
/a~1b |
"a" |
/inexistent/path |
error |
You can find more details about the structure of absolute JSON pointers here.
Using absolute pointers
You can use absolute JSON pointers to fetch data/subschemas defined in the document.
You must specify the pointer in the fragment component of the URI, this means
that you should place your pointer after #
.
Consider the following JSON schema document
{
"$id": "http://example.com/schema.json#",
"$defs": {
"name": {
"type": "string",
"minLength": 1
},
"personal": {
"email": {
"type": "string",
"format": "email"
},
"birthday": {
"type": "string",
"format": "date"
}
}
}
}
Here is a table containing the absolute URI and pointer to fetch the desired schemas.
Absolute URI and pointer | Fetched schema |
---|---|
http://example.com/schema.json# |
the document itself |
http://example.com/schema.json#/ |
the document itself |
http://example.com/schema.json#/$defs/name |
{"type": "string", "minLength": 1} |
http://example.com/schema.json#/$defs/personal/email |
{"type": "string", "format": "email"} |
http://example.com/schema.json#/$defs/personal/birthday |
{"type": "string", "format": "date"} |
http://example.com/schema.json#/inexistent/path |
error |
Now lets see a complex example that uses the $ref
keyword.
{
"$id": "http://example.com/path/to/user.json",
"type": "object",
"properties": {
"email": {
"$ref": "#/$defs/personal/email"
},
"birthday": {
"$ref": "#/$defs/personal/birthday"
},
"settings": {
"$ref": "user-settings.json#/$defs/settings"
},
"info": {
"$ref": "../info.json#"
},
"root": {
"$ref": "/other/path/to/schema.json#/$defs/root"
},
"external": {
"$ref": "http://external.example.com/some-schema.json#/$defs/name"
}
},
"$defs": {
"personal": {
"email": {
"type": "string",
"format": "email"
},
"birthday": {
"type": "string",
"format": "date"
}
}
}
}
First thing we have to notice is that this document contains the $id
keyword.
This means that all values of the $ref
keyword will be resolved using the
$id
as base.
The following table contains the resolved (absolute) URIs for $ref
s inside
the properties
keyword.
Property name | Resolved $ref |
---|---|
http://example.com/path/to/user.json#/$defs/personal/email |
|
birthday | http://example.com/path/to/user.json#/$defs/personal/birthday |
settings | http://example.com/path/to/user-settings.json#/$defs/settings |
info | http://example.com/path/info.json# |
root | http://example.com/other/path/to/schema.json#/$defs/root |
external | http://external.example.com/some-schema.json#/$defs/name |
These are the steps in order to perform validation for email property
- Get the value of
$ref
=>#/$defs/personal/email
- Get the absolute URI, using
$id
as base =>http://example.com/path/to/user.json#/$defs/personal/email
- Load the schema document having the
$id
equal tohttp://example.com/path/to/user.json
(in this case it is the same document) - Apply the json pointer
/$defs/personal/email
to get the subschema =>{"type": "string", "format": "email"}
- Use the subschema for validation (in our case we validate the value of email property)
Relative pointers
Relative JSON pointers are used to search for values starting at the current
location. We can go upwards by specifying the numbers of levels to ascend,
we can optionally change index position,
and then we can go downwards by using a pointer composed by multiple property names (keys) separated
by /
(slash). The level and the pointer are also separated by /
(slash).
Additionally, we can append #
which will return the property name (key)
where our value was found (this is very useful for arrays).
The level is always
required and must be a non-negative integer. Level 0
points to the current location,
level 1
points to the parent of current location, and so on.
You cannot use a level that will go past document root.
If a property name contains
~
, then inside the pointer it must be escaped using ~0
, or if contains /
then
it must be escaped using ~1
.
Here are some examples about how relative JSON pointers work:
{
"name": "some product",
"price": 10.5,
"features": [
"easy to use",
{
"name": "environment friendly",
"url": "http://example.com"
}
],
"info": {
"onStock": true
},
"a/b": "a"
}
Considering that our current location is 10.5
(absolute JSON pointer /price
)
we have the following table
Pointer | Value |
---|---|
0 |
10.5 |
0# |
"price" |
1 |
the document itself (root) |
1# |
error |
1/name |
"some product" |
1/info |
{"onStock": true} |
1/info/onStock |
true |
1/a~1b |
"a" |
1/inexstent/path |
error |
2 |
error |
Considering that our current location is "http://example.com"
(absolute JSON pointer /features/1/url
)
we have the following table
Pointer | Value |
---|---|
0 |
"http://example.com" |
0# |
"url" |
1# |
1 (array index) |
1/name |
"environment friendly" |
2# |
"features" |
2/0 |
"easy to use" |
1-1 |
"easy to use" |
2/0# |
0 (array index) |
3 |
the document itself (root) |
3/price |
10.5 |
3/info/onStock |
true |
3/inexstent/path |
error |
3# |
error |
4 |
error |
You can find more details about the structure of relative JSON pointers here.
Using relative pointers
You can use absolute JSON pointers to fetch data/subschemas defined in the document. You cannot use relative pointers in the way you use absolute pointers, so you cannot use them in an URI.
Here is an example using the $ref
keyword.
{
"type": "object",
"properties": {
"first_email": {
"type": "string",
"format": "email"
},
"second_email": {
"$ref": "1/first_email"
}
}
}
Input | Status |
---|---|
{"first_email": "john@example.com", "second_email: "opis@example.com"} |
valid |
{"second_email: "opis@example.com"} |
valid |
{"first_email": "john@example.com", "second_email: "invalid-email"} |
invalid |
{"second_email: "invalid-email"} |
invalid |
These are the steps taken in order to perform validation of second_email
property:
- The current location is
{"$ref": "1/first_email"}
(the value ofsecond_email
property) - Ascending
1
level we end up at the value ofproperties
property - Descending into
first_email
and we get{"type": "string", "format": "email"}
- Use the subschema to validate the value of
second_email
property