Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions content/2019-09/validation/type.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ JSON parsers.
| `"string"` | A JSON string | [UTF-8](https://en.wikipedia.org/wiki/UTF-8) Unicode encoding |

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

{{<best-practice>}} To avoid interoperability issues, do not produce JSON
documents with numbers that exceed the [IETF RFC
Expand Down
162 changes: 162 additions & 0 deletions content/draft4/core/id.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,165 @@ related:
- vocabulary: core
keyword: $schema
---

The [`id`]({{< ref "draft4/core/id" >}}) keyword explicitly turns a schema
into a _schema resource_ (a schema that is associated with a URI). Relative
URIs are resolved against the _current_ base URI, which is either the closest
parent [`id`]({{< ref "draft4/core/id" >}}) keyword (applicable in the case
of compound schemas), or the base URI as determined by the context on which the
schema is declared (i.e. serving a schema over HTTP _may_ implicitly award it
such URL as the base).

{{<learning-more>}}

This keyword directly applies (without modifications or extensions) the concept
of URIs to schemas. **If you are having a hard time following the concepts
discussed in this page, it is probably because you don't have a strong grasp of
URIs yet** (a notably hard but universal pre-requisite!).

To learn more about URIs, we strongly suggest studying the [IETF RFC
3986](https://www.rfc-editor.org/info/rfc3986) URI standard. To avoid
confusion, note that there is also a [WHATWG URL
Standard](https://url.spec.whatwg.org) that targets URLs in the context of web
browsers. However, JSON Schema only implements and expects the IETF original
standard.

{{</learning-more>}}

{{<common-pitfall>}}

In JSON Schema, schema identifiers are merely identifiers and no behaviour is
imposed on them. In particular, JSON Schema does not guarantee that a schema
with an HTTP URL identifier is actually resolvable at such URL. To avoid
surprises, JSON Schema implementations must be careful with automatically
sending remote network requests when encountering supposely resolvable schema
identifiers.

{{</common-pitfall>}}

{{<best-practice>}}

It is strongly recommended for every schema file to explicitly declare an
_absolute_ URI using this keyword. By doing so, you completely avoid various
complex URI resolution edge cases, mainly when the base URI is implicit and
context-dependent.

If you are serving schemas over the network (i.e. over HTTP), it is common to
set this keyword to the target URL. However, if your schemas are not accessible
over the network, prefer using a
[URN](https://en.wikipedia.org/wiki/Uniform_Resource_Name) (with a valid
namespace registered by
[IANA](https://www.iana.org/assignments/urn-namespaces/urn-namespaces.xhtml))
or a non-locatable URI scheme such as a [Tag URI](https://www.taguri.org).

{{</best-practice>}}

To debug the role of the [`id`]({{< ref "draft4/core/id" >}}) keyword on
a schema (particularly schemas with embedded resources), try the [`jsonschema
inspect`](https://github.com/sourcemeta/jsonschema/blob/main/docs/inspect.markdown)
command. This command prints detailed information about each schema resource,
subschema, location, and reference present in the schema. For example:

```sh
$ jsonschema inspect schema.json
(RESOURCE) URI: https://example.com/schema
Type : Static
Root : https://example.com/schema
Pointer :
Base : https://example.com/schema
Relative Pointer :
Dialect : http://json-schema.org/draft-04/schema#
Base Dialect : http://json-schema.org/draft-04/schema#
Parent : <NONE>
Instance Location :

...

(SUBSCHEMA) URI: https://example.com/schema#/properties/foo
Type : Static
Root : https://example.com/schema
Pointer : /properties/foo
Base : https://example.com/schema
Relative Pointer : /properties/foo
Dialect : http://json-schema.org/draft-04/schema#
Base Dialect : http://json-schema.org/draft-04/schema#
Parent :
Instance Location : /foo

...

(REFERENCE) ORIGIN: /$schema
Type : Static
Destination : http://json-schema.org/draft-04/schema
- (w/o fragment) : http://json-schema.org/draft-04/schema
- (fragment) : <NONE>
```

## Examples

{{<schema `A schema that declares a potentially resolvable HTTP absolute URL identifier`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/schemas/even-number.json",
"type": "number",
"multipleOf": 2
}
{{</schema>}}

{{<schema `A schema that declares a non-resolvable Tag URI identifier`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "tag:example.com,2025:even-number",
"type": "number",
"multipleOf": 2
}
{{</schema>}}

{{<schema `A schema that declares a non-resolvable URN example identifier`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "urn:example:even-number",
"type": "number",
"multipleOf": 2
}
{{</schema>}}

{{<schema `A schema that uses fragment identifiers to create reusable anchors`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/schemas/user.json",
"type": "object",
"properties": {
"name": { "$ref": "#nonEmptyString" },
"email": { "$ref": "#nonEmptyString" }
},
"definitions": {
"nonEmptyString": {
"id": "#nonEmptyString",
"type": "string",
"minLength": 1
}
}
}
{{</schema>}}

{{<schema `A compound schema that declares relative and absolute nested URIs`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/schemas/root.json",
"properties": {
"foo": {
"id": "foo.json"
},
"bar": {
"id": "/schemas/bar.json"
},
"baz": {
"id": "https://absolute.example/baz.json",
"items": {
"id": "deep"
}
}
}
}
{{</schema>}}
207 changes: 207 additions & 0 deletions content/draft4/core/ref.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,210 @@ related:
- vocabulary: validation
keyword: definitions
---

The [`$ref`]({{< ref "draft4/core/ref" >}}) keyword enables a schema to
reference another schema by its URI, effectively importing its keywords into the
current evaluation process. This keyword is the cornerstone of schema
composition, allowing complex schemas to be created out of simpler ones. A
reference may set its URI fragment to a [JSON
Pointer](https://www.rfc-editor.org/rfc/rfc6901) that determines the destination
of the reference after first resolving the rest of the URI.

{{<common-pitfall>}}

In JSON Schema [Draft 7](/draft7) and earlier versions, **any subschema
declaring the [`$ref`]({{< ref "draft4/core/ref" >}}) keyword is considered to
be a _reference object_ and any other sibling keyword is silently ignored**. As
a consequence, subschemas with references that make use of other keywords must
artificially wrap the reference into its own subschema using keywords like
[`allOf`]({{< ref "draft4/validation/allof" >}}).

{{</common-pitfall>}}

{{<common-pitfall>}}

**Avoid referencing other schema files using their file paths**. While some
implementations support this by automatically constructing schema URIs that
make use of the `file://` scheme, this is not enforced behaviour. The only
standard and guaranteed mechanism of declaring a schema URI for identification
and referencing purposes is through the [`id`]({{< ref "draft4/core/id" >}}) keyword.

{{</common-pitfall>}}

{{<common-pitfall>}}

The target of a reference must be a schema. Referencing a JSON value that is
not unambiguously recognised as a schema leads to undefined behaviour. This
not only includes referencing arbitrary JSON files (the obvious case), but
also referencing parts of a schema that a JSON Schema evaluator does not
consider to be a subschema.

{{</common-pitfall>}}

References are either _internal_ (pointing at schemas within the same schema
definition) or _external_ (pointing at schema resources outside the given schema
definition). If the reference is a relative URI, it is resolved against the
_current_ base URI, which is either the closest parent URI as set by the
[`id`]({{< ref "draft4/core/id" >}}) keyword, or the base URI as determined by
the context on which the schema is declared. Schema wrappers like OpenAPI are
notable examples of the latter. A relative reference from a schema embedded in
an OpenAPI specification is resolved from the root of the API specification, and
not from the root of the schema.

{{<best-practice>}}

It is highly recommended to make use of _external_ references to break down
complex monolithic schemas into smaller schema files. However, for performance
and integrity reasons, avoid resolving these external schemas (i.e. over HTTP
or the filesystem) at runtime.

You can automatically inline external references at build time using the
[`jsonschema
bundle`](https://github.com/sourcemeta/jsonschema/blob/main/docs/bundle.markdown)
command.

{{</best-practice>}}

Note that a reference to an absolute URI does not necessarily mean that the
reference is external. Conversely, a reference to a relative URI does not
necessarily mean that the reference is internal. When encountering any type
of reference, a JSON Schema implementation will check if the root schema
resource or its nested schema resources (if any) declare the canonically
resolved version of such URI through the [`id`]({{< ref "draft4/core/id"
>}}) keyword. If so, the reference is considered internal. This
internal-first lookup is what enables the standard
[bundling](https://json-schema.org/blog/posts/bundling-json-schema-compound-documents)
process.

{{<learning-more>}}

If you are having a hard time understanding references and some of its more
subtle scenarios (like base URI resolution), it is probably because you don't
have a strong grasp of URIs yet (a notably hard but universal
pre-requisite!).

To learn more about URIs, we strongly suggest studying the [IETF RFC
3986](https://www.rfc-editor.org/info/rfc3986) URI standard. To avoid
confusion, note that there is also a [WHATWG URL
Standard](https://url.spec.whatwg.org) that targets URLs in the context of
web browsers. However, JSON Schema only implements and expects the IETF
original standard. As a notable extension, this keyword supports referencing
specific parts of a schema through the use of a JSON Pointer, so we also
recommend studying the [IETF RFC 6901](https://www.rfc-editor.org/info/rfc6901)
JSON Pointer standard and its [URI fragment identifier
representation](https://www.rfc-editor.org/rfc/rfc6901#section-6).

{{</learning-more>}}

To debug references and how JSON Schema is interpreting your relative URIs,
try the [`jsonschema
inspect`](https://github.com/sourcemeta/jsonschema/blob/main/docs/inspect.markdown)
command. This command prints detailed information about each schema reference
and of each location of the schema. For example:

```sh
$ jsonschema inspect schema.json
...

(REFERENCE) ORIGIN: /properties/foo/$ref
Type : Static
Destination : https://example.com/schemas/example#/definitions/uuid
- (w/o fragment) : https://example.com/schemas/example
- (fragment) : /definitions/uuid

...
```

## Examples

{{<schema `A schema whose keywords are mostly ignored given a non-wrapped sibling reference`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"foo": {
"type": "string",
"$ref": "#/definitions/test"
}
},
"definitions": {
"test": { "minLength": 2 }
}
}
{{</schema>}}

{{<instance-pass `A non-string value is valid as the reference overrides the type declaration`>}}
12345
{{</instance-pass>}}

{{<instance-pass `A string value with a length equal or higher than 2 is valid`>}}
"foo"
{{</instance-pass>}}

{{<instance-fail `A string value with a length less than 2 is invalid`>}}
"x"
{{</instance-fail>}}

{{<schema `A schema that internally references the exact same helper schema in multiple equivalent ways`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/my-schema",
"properties": {
"byRelativeFragmentPointer": {
"$ref": "#/definitions/helper"
},
"byAbsoluteFragmentPointer": {
"$ref": "https://example.com/my-schema#/definitions/helper"
},
"byRelativeURI": {
"$ref": "my-helper"
},
"byRelativeRootPathURI": {
"$ref": "/my-helper"
},
"byRelativeBackslashURI": {
"$ref": "my-schema/../my-helper"
},
"byAbsoluteURI": {
"$ref": "https://example.com/my-helper"
}
},
"definitions": {
"helper": {
"id": "my-helper",
"type": "string"
}
}
}
{{</schema>}}

{{<schema `A schema that externally references the exact same schema URL in multiple equivalent ways`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/my-schema",
"properties": {
"byAbsoluteURI": {
"$ref": "https://example.com/my-other-schema"
},
"byRelativeURI": {
"$ref": "my-other-schema"
},
"byRelativeRootPathURI": {
"$ref": "/my-other-schema"
},
"byRelativeBackslashURI": {
"$ref": "my-schema/../my-other-schema"
}
}
}
{{</schema>}}

{{<schema `A schema that externally references a schema URN in the only possible way (URNs are always absolute)`>}}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"byAbsoluteURI": {
"$ref": "urn:example:my-other-schema"
}
}
}
{{</schema>}}
Loading