Skip to content

Commit fefe6c5

Browse files
authored
Fully document JSON Schema Draft 6 (#302)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 3e9330b commit fefe6c5

40 files changed

+3097
-12
lines changed

content/2019-09/format/format.markdown

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ annotation:
2020
kind: [ "string" ]
2121
---
2222

23-
The `format` keyword communicates that string instances are of the given
24-
logical type by producing an annotation value.
23+
The [`format`]({{< ref "2019-09/format/format" >}}) keyword communicates that
24+
string instances are of the given logical type by producing an annotation
25+
value.
2526

2627
{{<common-pitfall>}} By default, this keyword does not perform validation, as
2728
validating formats is considered optional by the official JSON Schema Test

content/draft6/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 "draft6/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 "draft6/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 "draft6/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-06/schema#
89+
Base Dialect : http://json-schema.org/draft-06/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-06/schema#
102+
Base Dialect : http://json-schema.org/draft-06/schema#
103+
Parent :
104+
Instance Location : /foo
105+
106+
...
107+
108+
(REFERENCE) ORIGIN: /$schema
109+
Type : Static
110+
Destination : http://json-schema.org/draft-06/schema
111+
- (w/o fragment) : http://json-schema.org/draft-06/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-06/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-06/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-06/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-06/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-06/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/draft6/core/ref.markdown

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

0 commit comments

Comments
 (0)