References
References can be used to re-use parts/objects of JSON-Schema definitions.
Supported reference types
internal (in a single file) self-reference to the enclosing scope, for recursive schemas (example: “$ref”: “”, equivalent to “$ref”: “#”)
internal (in a single file) reference by id (example: “$ref”: “#IdOfMyObject”)
internal (in a single file) reference by path using
definitions(Draft 7, example: “$ref”: “#/definitions/myObject”)internal (in a single file) reference by path using
$defs(Draft 2019-09, example: “$ref”: “#/$defs/myObject”)relative reference based on the location on the file system to a complete file (example: “$ref”: “./../modules/myObject.json”)
relative reference based on the location on the file system to an object by id (example: “$ref”: “./../modules/myObject.json#IdOfMyObject”)
relative reference based on the location on the file system to an object by path (example: “$ref”: “./../modules/myObject.json#/definitions/myObject” or “$ref”: “./../modules/myObject.json#/$defs/myObject”)
absolute reference based on the location on the file system to a complete file (example: “$ref”: “/modules/myObject.json”)
absolute reference based on the location on the file system to an object by id (example: “$ref”: “/modules/myObject.json#IdOfMyObject”)
absolute reference based on the location on the file system to an object by path (example: “$ref”: “/modules/myObject.json#/definitions/myObject” or “$ref”: “/modules/myObject.json#/$defs/myObject”)
network reference to a complete file (example: “$ref”: “https://my.domain.com/schema/modules/myObject.json”)
network reference to an object by id (example: “$ref”: “https://my.domain.com/schema/modules/myObject.json#IdOfMyObject”)
network reference to an object by path (example: “$ref”: “https://my.domain.com/schema/modules/myObject.json#/definitions/myObject” or “$ref”: “https://my.domain.com/schema/modules/myObject.json#/$defs/myObject”)
If an $id is present in the schema, the $ref will be resolved relative to the $id (except the $ref already is an absolute reference, e.g. a full URL). The behaviour of $ref resolving can be overwritten by implementing a custom SchemaProviderInterface, for example when you want to use network references behind an authorization.
Note
For absolute local references, the default implementation traverses up the directory tree until it finds a matching file to find the project root
Object reference
An example for properties referring to a definition inside the same schema (Draft 7 using definitions):
{
"definitions": {
"person": {
"$id": "#person",
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"$id": "team",
"type": "object",
"properties": {
"leader": {
"$ref": "#person"
}
"members": {
"type": "array",
"items": {
"$ref": "#/definitions/person"
}
}
}
}
Draft 2019-09 introduced $defs as the standard replacement for definitions. Both keywords are supported and behave identically:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"person": {
"$id": "#person",
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"$id": "team",
"type": "object",
"properties": {
"leader": {
"$ref": "#person"
},
"members": {
"type": "array",
"items": {
"$ref": "#/$defs/person"
}
}
}
}
Self-reference (recursive schemas)
An empty $ref ("$ref": "") references the enclosing document or $id scope itself,
without needing to name it. This is the standard idiom for recursive schemas, e.g. an object that
may contain further objects of its own type:
{
"$id": "#person",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": ""
}
}
}
}
"" resolves against the nearest enclosing $id (or the document root if none is in
scope) — a $ref: "" written inside a $id-scoped definition recurses into that definition,
not into the top-level document. It behaves identically to explicitly writing the enclosing
scope’s own id ("$ref": "#person" above), just without needing to name it.
Using an empty (or "#") $ref at a schema’s own base level (see Base Reference below)
is rejected instead: it would ask the schema to be merged with itself, which has no fixed point
to compute.
Base Reference
The whole model may contain a reference. In this case all base validations (eg. number of allowed attributes) must be defined in the referenced schema.
{
"definitions": {
"person": {
"$id": "#person",
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"$id": "#Citizen",
"$ref": "#person"
}
The same pattern works with $defs (Draft 2019-09):
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"person": {
"$id": "#person",
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
},
"$id": "#Citizen",
"$ref": "#person"
}
Generated interface:
// class Citizen
public function setName(string $name): static;
public function getName(): ?string;
If a base reference is used and the reference doesn’t point to an object definition an Exception will be thrown during the model generation process:
A referenced schema on base level must provide an object definition [Citizen]
A base reference must also not reference the schema’s own root ("$ref": "" or "$ref": "#"
at base level) — an Exception will be thrown during the model generation process:
A referenced schema on base level must not reference itself [Citizen]
$ref with sibling keywords
When additional keywords appear alongside $ref in the same schema object — such as
properties, required, type, minLength, enum, or others — their treatment
depends on the active JSON Schema draft.
- Draft 2019-09+ (siblings applied)
Sibling keywords take full effect alongside
$ref. At the base/root level, siblingpropertiesandrequiredare merged with the referenced object’s own properties — constraints from both sides apply (type intersection, required union). At the property level, sibling constraints (type,minLength,enum, etc.) narrow the referenced type.- Draft 7 (siblings silently ignored)
Sibling keywords are discarded. Only the
$refis effective. This matches the Draft 7 specification, where$refis exclusive over all co-located keywords.Note
If your schemas rely on siblings alongside
$refand you switch from Draft 7 to Draft 2019-09 (or useAutoDetectionDraftwith a$schemaURI pointing to 2019-09), those siblings will start taking effect. Review schemas that combine$refwith extra keywords before upgrading.
Example — base-level $ref + sibling properties (Draft 2019-09+)
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"person": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"required": ["name"]
}
},
"$ref": "#/$defs/person",
"properties": {
"street": {
"type": "string"
}
},
"required": ["name", "street"]
}
The generated class exposes name and age (from the ref) together with street
(from the sibling properties):
public function getName(): ?string;
public function getAge(): ?int;
public function getStreet(): ?string;
When a property is declared by both the $ref’d object and the sibling properties
block, the two declarations are merged with allOf semantics: types narrow to their
intersection (for example, number ∩ integer → int), narrowing never drops the
$ref’d side’s own constraints (e.g. minimum) — they’re re-applied against the narrowed
type — and conflicting default values cause a generation-time SchemaException. This merge
behavior is uniform across every $ref``+sibling position: base level, a property-level
``$ref to an object, and a property-level $ref to a scalar all merge the same way.
Example — property-level $ref + sibling constraint (Draft 2019-09+)
A property schema with $ref pointing to a string definition and a sibling minLength:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"code": {
"$ref": "#/$defs/shortString",
"minLength": 5
}
},
"$defs": {
"shortString": {
"type": "string",
"minLength": 1
}
}
}
Both constraints are enforced: the effective minimum length is 5 (the sibling tightens
the ref’s minLength: 1).