JSON alone can only capture tree structured objects. JSON Reference (or JsonRef) is a standard way to represent references in JSON documents, to allow graph structured objects to be stored as JSON. Conceptually, JSON Reference extends pure JSON decoding/encoding by parsing any JSON reference ($ref
) occurrences to native language dependent reference types after decoding, and normalizing references in a JSON reference compatible way before encoding (some implementations may only support dereferencing):
object
___
.-------------. | |\ .---------.
.------->| Normalize |--->| '-|--->| Encode |-------.
| | | | | | | |
| '-------------' |_____| '---------' |
| v
object object string
___ ___ ___
(\_/) | |\ .-------------. | |\ .---------. | |\
(O.o) | '-| | Dereference | | '-| | Decode | | '-|
(> <) | |<----| |<---| |<---| |<---| |
|_____| '-------------' |_____| '---------' |_____|
This specification defines JSON Reference v0.4.0. JSON Reference v0.4.0 succeeds JSON Reference v0.3.0 and is not entirely backwards compatible. JSON Reference v0.4.0 relies on the JSON Pointer v0.4.0 specification. This specification is hosted on Github.
Every valid JSON Reference document is a valid JSON document. JSON Reference makes use of four special object properties meaningful to JSON Reference, but not meaningful to JSON: $ref
, $id
and optionally $refProp
, $idProp
:
$id
property is optional on any object. $id
is used to uniquely identify a sub object within the scope of a given JSON document.$id
property is analogous to the name
or id
HTML attribute.$id
property values must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens (“-“), underscores (“_”), colons (“:”), and periods (“.”)”, except in the following case:$id
occurring at the root of a JSON document, and only the root, to be an absolute URI.$id
property values are case sensitive.$id
property values must be unique within a JSON document. Implementations must raise an error if duplicate $id
values are present within a single JSON Document.$ref
property, such as { "$ref": <URI> }
, are entirely replaced by the value pointed to by <URI>
. This value is called the replacement-value.$ref
key are ignored.<URI>
must be a valid URI. Implementations must attempt to resolve the <URI>
to a replacement-value as follows:<URI>
must be:
$id
property value immediately followed by an optional JSON Pointer (example: “#a/b/c”),<URI>
consists only of a fragment identifier component (example: “#frag”):
$id
property value immediately followed by an optional JSON Pointer (case 2 above), the JSON Pointer must be resolved relative to the object in the current document with the given $id
property.Example:
{ “a”: { “$id”: “x”, “b”: 1 }, “b”: 2, “c”: { “$ref”: “#x/b” }, “d”: { “$ref”: “#/b” } }
should give the following after dereferencing:
{ “a”: { “$id”: “x”, “b”: 1 }, “b”: 2, “c”: 1 “d”: 2 }
<URI>
is not just a fragment identifier component (example: “/some/path#frag”), implementations should first resolve relative URIs against a base URI as described in RFC 3986, then use the value identified by the absolute URI as the replacement-value. How the base URI is determined is beyond the scope of this specification.$idProp
, and $refProp
properties optionally allow for different property names to be used for “$id”, and “$ref” .$idProp
and $refProp
must be set on the JSON document root. The property values must be valid JSON property names.Example:
{ “$idProp”: “$id.607cc38b5ff40”, “$refProp”: “$ref.607cc3a1c764b”, “a”: { “$id.607cc38b5ff40”: “a”, “foo”: “bah”, “…”: “…” }, “b”: { “a”: { “$ref.607cc3a1c764b”: “#a” } } }
Lazy dereferencing of $ref
objects in a decoded JSON document may be supported. Lazy dereferencing attempts to dereference $ref
objects “as needed”. How “as needed” is defined is dependent on the application. All the above rules still apply. The only difference between lazy and canonical (upfront) dereferencing is when errors are detected and raised to the client. Implementations that use or support lazy dereferencing should make it clear when, how, and if lazy dereferencing is being performed.
This JSON Reference specification provides no strict guidance on how to encode native objects containing references to JSON Reference compatible JSON documents. Of course, this can still be done. The only requirement is that the resulting JSON is actually encodable (tree structured) and uses Json Reference keywords in a manner consistent with this specification.
JSON Reference is not JSON Merge or JSON Patch and supporting object merging is beyond the scope of the specification. The focus of the specification is simply encoding objects with references to themselves.
JSON Reference is not JSON Schema draft v06+’s JSON Reference like dereferencing implementation, although it has been suggested as a replacement for it.
Like JSON Schema draft v06+ specifications, this implementation breaks with the original JSON Reference specifications in using $id
(by default) instead of id
for object identifiers. However, this specification diverges with the canonical JSON Schema specification of references:
$id
does not establish the base URI of the document. Instead, a base URI should be provided by the client, either explicitly, or as the URI from which the resource was loaded.$id
keyword does not change the base URI in anyway. By default, each distinct document has a base URI established for it in some way beyond the scope of this specification, and any relative URI encountered should be qualified against this document wide base URI (resolving to a valid absolute URI, does not imply the identified resource should then be retrieved).$ref
can occur or what a $ref
can refer to.According to JSON Reference v0.3.0, the JSON reference fragment part must be a JSON Pointer. Another commonly implemented type of reference is a reference to an object that has an $id
field as described above. JSON Schema for example, requires such references. The specification extends the previous specification by adding explicit support for this reference type (described above). Example:
{
"foo": "bah",
"a": {
"$id": "#foo"
},
"b": {
"byid": { "$ref": "#foo" },
"byref": { "$ref": "#/foo" }
}
}
Gives:
...
"b": {
"byid": { "$id: "#foo" },
"byref": "bah"
}
In general circular references are allowed and useful for defining graph like structures - which is the whole point in JSON Reference. However, consider:
{
"foo": { "$ref": "#/bah" },
"bah": { "$ref": "#/foo" }
}
{ "$ref": "#/ }
Neither document makes sense because they are vacuous: the references are expected to refer to valid content, they are not content in an of themselves, so in these cases no valid value can ever be resolved, and the document is erroneous (raise error). On the other hand, all of the following are fine:
{
"foo": { "$ref": "#/bah" },
"bah": { "$ref": "#/" }
}
{
"foo": { "$ref": "#/" }
}
{
"definitions": {
"foo": { "properties": { "bar": { "$ref": "#/definitions/bar" } } },
"bar": { "properties": { "foo": { "$ref": "#/definitions/foo" } } }
},
"type": "object",
"properties": { "foo": { "$ref": "#/definitions/foo" } }
}
In summary, pointers to pointers are fine, but pointers to pointers resulting in a pure pointer loop are erroneous. Conceptually, if one can collapse any pointer to pointer chain into an eventual non pointer replacement-value dereferencing should succeed, if not an error must be raised.
Consider the following document:
{
"a": {
"x": { "$ref": "#/b/x" }
},
"b": { "$ref": "#/c" },
"c": {
"x": "Hey you found me!"
}
}
#/b/x
points through the reference at #/b
. For this to work, we must ensure #/b
is resolved before #/b/x
.
This:
{
"a": 1,
"b": { "$ref": "#/a" }
}
should give a dereferenced object like:
{
"a": 1,
"b": 1
}
Whether a
and b
actually reference the same storage location or a copy is implementation dependent (as explained above). Implementations may choose to allow the client to configure either behavior.
It’s often the case that one JSON Reference document will refer to parts of, or all of another document identified by a absolute URI. In such cases it is convenient to distribute all the required documents in a single “bundle”. Bundling is also a way to avoid having to load required documents from remote locations at dereferencing time. A suggested solution is to simply bundle multiple documents either:
$id
field set to the URI of the document.$id
need not be specified).It is upto the implementation to identify and parse such bundles and load resource from the bundle rather than remote resource locations. Clients should be required to explicitly request this loading behaviour.
The above does not imply other bundling and/or local caching solutions are prohibited.