Skip to content

Commit 5ea1085

Browse files
authored
Fully document JSON Schema Draft 4 (#303)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent fefe6c5 commit 5ea1085

39 files changed

+3110
-23
lines changed

content/2019-09/validation/type.markdown

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,11 @@ JSON parsers.
3737
| `"string"` | A JSON string | [UTF-8](https://en.wikipedia.org/wiki/UTF-8) Unicode encoding |
3838

3939
Note that while the JSON grammar does not distinguish between integer and real
40-
numbers, JSON Schema provides the [`integer`]({{< ref
41-
"2019-09/validation/type" >}}) logical type that matches either integers (such
42-
as `2`), or real numbers where the fractional part is zero (such as `2.0`).
43-
Additionally, numeric constructs inherent to floating point encodings (like
44-
`NaN` and `Infinity`) are not permitted in JSON. However, the negative zero
45-
(`-0`) is permitted.
40+
numbers, JSON Schema provides the `integer` logical type that matches either
41+
integers (such as `2`), or real numbers where the fractional part is zero (such
42+
as `2.0`). Additionally, numeric constructs inherent to floating point
43+
encodings (like `NaN` and `Infinity`) are not permitted in JSON. However, the
44+
negative zero (`-0`) is permitted.
4645

4746
{{<best-practice>}} To avoid interoperability issues, do not produce JSON
4847
documents with numbers that exceed the [IETF RFC

content/draft4/core/id.markdown

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,165 @@ related:
1818
- vocabulary: core
1919
keyword: $schema
2020
---
21+
22+
The [`id`]({{< ref "draft4/core/id" >}}) keyword explicitly turns a schema
23+
into a _schema resource_ (a schema that is associated with a URI). Relative
24+
URIs are resolved against the _current_ base URI, which is either the closest
25+
parent [`id`]({{< ref "draft4/core/id" >}}) keyword (applicable in the case
26+
of compound schemas), or the base URI as determined by the context on which the
27+
schema is declared (i.e. serving a schema over HTTP _may_ implicitly award it
28+
such URL as the base).
29+
30+
{{<learning-more>}}
31+
32+
This keyword directly applies (without modifications or extensions) the concept
33+
of URIs to schemas. **If you are having a hard time following the concepts
34+
discussed in this page, it is probably because you don't have a strong grasp of
35+
URIs yet** (a notably hard but universal pre-requisite!).
36+
37+
To learn more about URIs, we strongly suggest studying the [IETF RFC
38+
3986](https://www.rfc-editor.org/info/rfc3986) URI standard. To avoid
39+
confusion, note that there is also a [WHATWG URL
40+
Standard](https://url.spec.whatwg.org) that targets URLs in the context of web
41+
browsers. However, JSON Schema only implements and expects the IETF original
42+
standard.
43+
44+
{{</learning-more>}}
45+
46+
{{<common-pitfall>}}
47+
48+
In JSON Schema, schema identifiers are merely identifiers and no behaviour is
49+
imposed on them. In particular, JSON Schema does not guarantee that a schema
50+
with an HTTP URL identifier is actually resolvable at such URL. To avoid
51+
surprises, JSON Schema implementations must be careful with automatically
52+
sending remote network requests when encountering supposely resolvable schema
53+
identifiers.
54+
55+
{{</common-pitfall>}}
56+
57+
{{<best-practice>}}
58+
59+
It is strongly recommended for every schema file to explicitly declare an
60+
_absolute_ URI using this keyword. By doing so, you completely avoid various
61+
complex URI resolution edge cases, mainly when the base URI is implicit and
62+
context-dependent.
63+
64+
If you are serving schemas over the network (i.e. over HTTP), it is common to
65+
set this keyword to the target URL. However, if your schemas are not accessible
66+
over the network, prefer using a
67+
[URN](https://en.wikipedia.org/wiki/Uniform_Resource_Name) (with a valid
68+
namespace registered by
69+
[IANA](https://www.iana.org/assignments/urn-namespaces/urn-namespaces.xhtml))
70+
or a non-locatable URI scheme such as a [Tag URI](https://www.taguri.org).
71+
72+
{{</best-practice>}}
73+
74+
To debug the role of the [`id`]({{< ref "draft4/core/id" >}}) keyword on
75+
a schema (particularly schemas with embedded resources), try the [`jsonschema
76+
inspect`](https://github.com/sourcemeta/jsonschema/blob/main/docs/inspect.markdown)
77+
command. This command prints detailed information about each schema resource,
78+
subschema, location, and reference present in the schema. For example:
79+
80+
```sh
81+
$ jsonschema inspect schema.json
82+
(RESOURCE) URI: https://example.com/schema
83+
Type : Static
84+
Root : https://example.com/schema
85+
Pointer :
86+
Base : https://example.com/schema
87+
Relative Pointer :
88+
Dialect : http://json-schema.org/draft-04/schema#
89+
Base Dialect : http://json-schema.org/draft-04/schema#
90+
Parent : <NONE>
91+
Instance Location :
92+
93+
...
94+
95+
(SUBSCHEMA) URI: https://example.com/schema#/properties/foo
96+
Type : Static
97+
Root : https://example.com/schema
98+
Pointer : /properties/foo
99+
Base : https://example.com/schema
100+
Relative Pointer : /properties/foo
101+
Dialect : http://json-schema.org/draft-04/schema#
102+
Base Dialect : http://json-schema.org/draft-04/schema#
103+
Parent :
104+
Instance Location : /foo
105+
106+
...
107+
108+
(REFERENCE) ORIGIN: /$schema
109+
Type : Static
110+
Destination : http://json-schema.org/draft-04/schema
111+
- (w/o fragment) : http://json-schema.org/draft-04/schema
112+
- (fragment) : <NONE>
113+
```
114+
115+
## Examples
116+
117+
{{<schema `A schema that declares a potentially resolvable HTTP absolute URL identifier`>}}
118+
{
119+
"$schema": "http://json-schema.org/draft-04/schema#",
120+
"id": "https://example.com/schemas/even-number.json",
121+
"type": "number",
122+
"multipleOf": 2
123+
}
124+
{{</schema>}}
125+
126+
{{<schema `A schema that declares a non-resolvable Tag URI identifier`>}}
127+
{
128+
"$schema": "http://json-schema.org/draft-04/schema#",
129+
"id": "tag:example.com,2025:even-number",
130+
"type": "number",
131+
"multipleOf": 2
132+
}
133+
{{</schema>}}
134+
135+
{{<schema `A schema that declares a non-resolvable URN example identifier`>}}
136+
{
137+
"$schema": "http://json-schema.org/draft-04/schema#",
138+
"id": "urn:example:even-number",
139+
"type": "number",
140+
"multipleOf": 2
141+
}
142+
{{</schema>}}
143+
144+
{{<schema `A schema that uses fragment identifiers to create reusable anchors`>}}
145+
{
146+
"$schema": "http://json-schema.org/draft-04/schema#",
147+
"id": "https://example.com/schemas/user.json",
148+
"type": "object",
149+
"properties": {
150+
"name": { "$ref": "#nonEmptyString" },
151+
"email": { "$ref": "#nonEmptyString" }
152+
},
153+
"definitions": {
154+
"nonEmptyString": {
155+
"id": "#nonEmptyString",
156+
"type": "string",
157+
"minLength": 1
158+
}
159+
}
160+
}
161+
{{</schema>}}
162+
163+
{{<schema `A compound schema that declares relative and absolute nested URIs`>}}
164+
{
165+
"$schema": "http://json-schema.org/draft-04/schema#",
166+
"id": "https://example.com/schemas/root.json",
167+
"properties": {
168+
"foo": {
169+
"id": "foo.json"
170+
},
171+
"bar": {
172+
"id": "/schemas/bar.json"
173+
},
174+
"baz": {
175+
"id": "https://absolute.example/baz.json",
176+
"items": {
177+
"id": "deep"
178+
}
179+
}
180+
}
181+
}
182+
{{</schema>}}

content/draft4/core/ref.markdown

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,210 @@ related:
2020
- vocabulary: validation
2121
keyword: definitions
2222
---
23+
24+
The [`$ref`]({{< ref "draft4/core/ref" >}}) keyword enables a schema to
25+
reference another schema by its URI, effectively importing its keywords into the
26+
current evaluation process. This keyword is the cornerstone of schema
27+
composition, allowing complex schemas to be created out of simpler ones. A
28+
reference may set its URI fragment to a [JSON
29+
Pointer](https://www.rfc-editor.org/rfc/rfc6901) that determines the destination
30+
of the reference after first resolving the rest of the URI.
31+
32+
{{<common-pitfall>}}
33+
34+
In JSON Schema [Draft 7](/draft7) and earlier versions, **any subschema
35+
declaring the [`$ref`]({{< ref "draft4/core/ref" >}}) keyword is considered to
36+
be a _reference object_ and any other sibling keyword is silently ignored**. As
37+
a consequence, subschemas with references that make use of other keywords must
38+
artificially wrap the reference into its own subschema using keywords like
39+
[`allOf`]({{< ref "draft4/validation/allof" >}}).
40+
41+
{{</common-pitfall>}}
42+
43+
{{<common-pitfall>}}
44+
45+
**Avoid referencing other schema files using their file paths**. While some
46+
implementations support this by automatically constructing schema URIs that
47+
make use of the `file://` scheme, this is not enforced behaviour. The only
48+
standard and guaranteed mechanism of declaring a schema URI for identification
49+
and referencing purposes is through the [`id`]({{< ref "draft4/core/id" >}}) keyword.
50+
51+
{{</common-pitfall>}}
52+
53+
{{<common-pitfall>}}
54+
55+
The target of a reference must be a schema. Referencing a JSON value that is
56+
not unambiguously recognised as a schema leads to undefined behaviour. This
57+
not only includes referencing arbitrary JSON files (the obvious case), but
58+
also referencing parts of a schema that a JSON Schema evaluator does not
59+
consider to be a subschema.
60+
61+
{{</common-pitfall>}}
62+
63+
References are either _internal_ (pointing at schemas within the same schema
64+
definition) or _external_ (pointing at schema resources outside the given schema
65+
definition). If the reference is a relative URI, it is resolved against the
66+
_current_ base URI, which is either the closest parent URI as set by the
67+
[`id`]({{< ref "draft4/core/id" >}}) keyword, or the base URI as determined by
68+
the context on which the schema is declared. Schema wrappers like OpenAPI are
69+
notable examples of the latter. A relative reference from a schema embedded in
70+
an OpenAPI specification is resolved from the root of the API specification, and
71+
not from the root of the schema.
72+
73+
{{<best-practice>}}
74+
75+
It is highly recommended to make use of _external_ references to break down
76+
complex monolithic schemas into smaller schema files. However, for performance
77+
and integrity reasons, avoid resolving these external schemas (i.e. over HTTP
78+
or the filesystem) at runtime.
79+
80+
You can automatically inline external references at build time using the
81+
[`jsonschema
82+
bundle`](https://github.com/sourcemeta/jsonschema/blob/main/docs/bundle.markdown)
83+
command.
84+
85+
{{</best-practice>}}
86+
87+
Note that a reference to an absolute URI does not necessarily mean that the
88+
reference is external. Conversely, a reference to a relative URI does not
89+
necessarily mean that the reference is internal. When encountering any type
90+
of reference, a JSON Schema implementation will check if the root schema
91+
resource or its nested schema resources (if any) declare the canonically
92+
resolved version of such URI through the [`id`]({{< ref "draft4/core/id"
93+
>}}) keyword. If so, the reference is considered internal. This
94+
internal-first lookup is what enables the standard
95+
[bundling](https://json-schema.org/blog/posts/bundling-json-schema-compound-documents)
96+
process.
97+
98+
{{<learning-more>}}
99+
100+
If you are having a hard time understanding references and some of its more
101+
subtle scenarios (like base URI resolution), it is probably because you don't
102+
have a strong grasp of URIs yet (a notably hard but universal
103+
pre-requisite!).
104+
105+
To learn more about URIs, we strongly suggest studying the [IETF RFC
106+
3986](https://www.rfc-editor.org/info/rfc3986) URI standard. To avoid
107+
confusion, note that there is also a [WHATWG URL
108+
Standard](https://url.spec.whatwg.org) that targets URLs in the context of
109+
web browsers. However, JSON Schema only implements and expects the IETF
110+
original standard. As a notable extension, this keyword supports referencing
111+
specific parts of a schema through the use of a JSON Pointer, so we also
112+
recommend studying the [IETF RFC 6901](https://www.rfc-editor.org/info/rfc6901)
113+
JSON Pointer standard and its [URI fragment identifier
114+
representation](https://www.rfc-editor.org/rfc/rfc6901#section-6).
115+
116+
{{</learning-more>}}
117+
118+
To debug references and how JSON Schema is interpreting your relative URIs,
119+
try the [`jsonschema
120+
inspect`](https://github.com/sourcemeta/jsonschema/blob/main/docs/inspect.markdown)
121+
command. This command prints detailed information about each schema reference
122+
and of each location of the schema. For example:
123+
124+
```sh
125+
$ jsonschema inspect schema.json
126+
...
127+
128+
(REFERENCE) ORIGIN: /properties/foo/$ref
129+
Type : Static
130+
Destination : https://example.com/schemas/example#/definitions/uuid
131+
- (w/o fragment) : https://example.com/schemas/example
132+
- (fragment) : /definitions/uuid
133+
134+
...
135+
```
136+
137+
## Examples
138+
139+
{{<schema `A schema whose keywords are mostly ignored given a non-wrapped sibling reference`>}}
140+
{
141+
"$schema": "http://json-schema.org/draft-04/schema#",
142+
"properties": {
143+
"foo": {
144+
"type": "string",
145+
"$ref": "#/definitions/test"
146+
}
147+
},
148+
"definitions": {
149+
"test": { "minLength": 2 }
150+
}
151+
}
152+
{{</schema>}}
153+
154+
{{<instance-pass `A non-string value is valid as the reference overrides the type declaration`>}}
155+
12345
156+
{{</instance-pass>}}
157+
158+
{{<instance-pass `A string value with a length equal or higher than 2 is valid`>}}
159+
"foo"
160+
{{</instance-pass>}}
161+
162+
{{<instance-fail `A string value with a length less than 2 is invalid`>}}
163+
"x"
164+
{{</instance-fail>}}
165+
166+
{{<schema `A schema that internally references the exact same helper schema in multiple equivalent ways`>}}
167+
{
168+
"$schema": "http://json-schema.org/draft-04/schema#",
169+
"id": "https://example.com/my-schema",
170+
"properties": {
171+
"byRelativeFragmentPointer": {
172+
"$ref": "#/definitions/helper"
173+
},
174+
"byAbsoluteFragmentPointer": {
175+
"$ref": "https://example.com/my-schema#/definitions/helper"
176+
},
177+
"byRelativeURI": {
178+
"$ref": "my-helper"
179+
},
180+
"byRelativeRootPathURI": {
181+
"$ref": "/my-helper"
182+
},
183+
"byRelativeBackslashURI": {
184+
"$ref": "my-schema/../my-helper"
185+
},
186+
"byAbsoluteURI": {
187+
"$ref": "https://example.com/my-helper"
188+
}
189+
},
190+
"definitions": {
191+
"helper": {
192+
"id": "my-helper",
193+
"type": "string"
194+
}
195+
}
196+
}
197+
{{</schema>}}
198+
199+
{{<schema `A schema that externally references the exact same schema URL in multiple equivalent ways`>}}
200+
{
201+
"$schema": "http://json-schema.org/draft-04/schema#",
202+
"id": "https://example.com/my-schema",
203+
"properties": {
204+
"byAbsoluteURI": {
205+
"$ref": "https://example.com/my-other-schema"
206+
},
207+
"byRelativeURI": {
208+
"$ref": "my-other-schema"
209+
},
210+
"byRelativeRootPathURI": {
211+
"$ref": "/my-other-schema"
212+
},
213+
"byRelativeBackslashURI": {
214+
"$ref": "my-schema/../my-other-schema"
215+
}
216+
}
217+
}
218+
{{</schema>}}
219+
220+
{{<schema `A schema that externally references a schema URN in the only possible way (URNs are always absolute)`>}}
221+
{
222+
"$schema": "http://json-schema.org/draft-04/schema#",
223+
"properties": {
224+
"byAbsoluteURI": {
225+
"$ref": "urn:example:my-other-schema"
226+
}
227+
}
228+
}
229+
{{</schema>}}

0 commit comments

Comments
 (0)