This document is also available in this non-normative format: diff to previous version
Copyright © 2010-2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
JSON [RFC4627] has proven to be a highly useful object serialization and messaging format. JSON-LD [JSON-LD] harmonizes the representation of Linked Data in JSON by outlining a common JSON representation format for expressing directed graphs; mixing both Linked Data and non-Linked Data in a single document. This document outlines an Application Programming Interface and a set of algorithms for programmatically transforming JSON-LD documents in order to make them easier to work with in programming environments like JavaScript, Python, and Ruby.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This document has been under development for over 18 months in the JSON for Linking Data Community Group. The document has recently been transferred to the RDF Working Group for review, improvement, and publication along the Recommendation track. The specification has undergone significant development, review, and changes during the course of the last 18 months.
There are currently five interoperable implementations of this specification. There is a fairly complete test suite and a live JSON-LD editor that is capable of demonstrating the features described in this document. While development on implementations, the test suite and the live editor will continue, they are believed to be mature enough to be integrated into a non-production system at this point in time with the expectation that they could be used in a production system within the next year.
It is important for readers to understand that the scope of this document is currently under debate and new features may be added to the specification. Existing features may be modified heavily or removed entirely from the specification upon further review and feedback from the broader community. This is a work in progress and publication as a First Public Working Draft does not require that all Working Group members agree on the content of the document.
This document was published by the RDF Working Group as an Editor's Draft. If you wish to make comments regarding this document, please send them to public-rdf-comments@w3.org (subscribe, archives). All feedback is welcome.
Publication as an Editor's Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is a detailed specification for an Application Programming Interface for the JSON-LD Syntax. The document is primarily intended for the following audiences:
To understand the basics in this specification you must first be familiar with JSON, which is detailed in [RFC4627]. You must also understand the JSON-LD Syntax [JSON-LD], which is the base syntax used by all of the algorithms in this document. To understand the API and how it is intended to operate in a programming environment, it is useful to have working knowledge of the JavaScript programming language [ECMA-262] and WebIDL [WEBIDL]. To understand how JSON-LD maps to RDF, it is helpful to be familiar with the basic RDF concepts [RDF-CONCEPTS].
There are a number of ways that one may participate in the development of this specification:
The JSON-LD Syntax specification [JSON-LD] outlines a language that may be used to express Linked Data in JSON. Often, it is useful to be able to transform JSON-LD documents so that they may be easily processed in a programming environment like JavaScript, Python or Ruby.
There are three major types of transformation that are discussed in this document; compaction, expansion, and RDF conversion.
Software algorithms are easiest to write when the data that they are processing have a regular form. Since information can be represented by JSON-LD in a variety of different ways, transforming all of these methods into a uniform structure allows the developer to simplify their processing code. For example, note that the following input uses only terms and is fairly compact:
var input1 = { "@context": "http://json-ld.org/contexts/person.jsonld" "name": "Manu Sporny", "homepage": "http://manu.sporny.org/" }
The next input example uses one IRI to express a property, but leaves the rest of the information untouched.
var input2 = {
"@context": "http://json-ld.org/contexts/person.jsonld"
"http://xmlns.com/foaf/0.1/name": "Manu Sporny",
"homepage": "http://manu.sporny.org/"
}
While both inputs are valid JSON-LD, writing a program to handle every permutation of possible inputs can be difficult, especially when the incoming context could change as well. To ensure that the data can be given a more uniform structure, JSON-LD introduces the notion of expansion. Expansion performs two important operations. The first is to expand all values that are IRIs to their fully expanded form. The second is to express all values in expanded form. To transform both inputs above to the same representation, the developer could do the following:
function expansionCallback(output) { console.log(output); } // the second parameter is 'null' because the developer does not wish to // inject another context value jsonld.expand(input1, null, expansionCallback); jsonld.expand(input2, null, expansionCallback);
The output for both calls above will be:
[{ "http://xmlns.com/foaf/0.1/name": [{ "@value": "Manu Sporny" }], "http://xmlns.com/foaf/0.1/homepage": [{ "@id": "http://manu.sporny.org/" }] }]
Note that in the example above; all context definitions have been removed, all term and prefixes have been expanded to full IRIs, and all literals are expressed in expanded form. While the output is more difficult for a human to read, it is easier for a software program to process because of its very regular structure.
While expansion expands a given input as much as possible, compaction performs the opposite operation - expressing a given input as succinctly as possible. While expansion is meant to produce something that is easy to process by software programs, compaction is meant to produce something that is easy to read by software developers. Compaction uses a developer-supplied context to compresses all IRIs to terms or prefixes, and compacts all literals expressed in expanded form as much as possible.
The following example expresses input that has already been fully expanded:
var expandedInput = [{ "http://xmlns.com/foaf/0.1/name": [{ "@value": "Manu Sporny" }], "http://xmlns.com/foaf/0.1/homepage": [{ "@id": "http://manu.sporny.org/" }] }]
A developer that wants to transform the data above into a more human-readable form, could do the following using the JSON-LD API:
function compactionCallback(output) { console.log(output); } jsonld.compact(expandedInput, "http://json-ld.org/contexts/person.jsonld", compactionCallback);
The following would be the result of the call above:
{ "@context": "http://json-ld.org/contexts/person.jsonld" "name": "Manu Sporny", "homepage": "http://manu.sporny.org/" }
Note that all of the terms have been compressed and the context has been injected into the output. While compacted output is most useful to humans, it can also be carefully used to generate structures that are easy to use for developers to program against as well.
JSON-LD can be used to losslessly express the RDF data model as described in the RDF Concepts document [RDF-CONCEPTS]. This ensures that data can be round-tripped from any RDF syntax, like N-Triples or TURTLE, without any loss in the fidelity of the data. Assume the following RDF input in N-Triples format:
var data = " <http://manu.sporny.org/about/#manu> <http://xmlns.com/foaf/0.1/name> \"Manu Sporny\" .\n <http://manu.sporny.org/about/#manu> <http://xmlns.com/foaf/0.1/homepage> <http://manu.sporny.org/> .";
A developer can use the JSON-LD API to transform the markup above into a JSON-LD document:
function conversionCallback(result) { console.log("JSON-LD Document: ", result); }; jsonld.fromRDF(data, conversionCallback, {"format": "ntriples"});
The following expanded output would be the result of the call above:
[{ "@id": "http://manu.sporny.org/about/#manu", "http://xmlns.com/foaf/0.1/name": [{ "@value": "Manu Sporny" }], "http://xmlns.com/foaf/0.1/homepage": [{ "@id": "http://manu.sporny.org/" }] }]
Note that the output above, could easily be compacted to produce the following using the technique outlined in the previous section:
{ "@context": "http://json-ld.org/contexts/person.jsonld", "@id": "http://manu.sporny.org/about/#manu", "name": "Manu Sporny", "homepage": "http://manu.sporny.org/" }
Transforming the node above back to RDF is as simple as calling the
toRDF()
method:
var jsonldDocument = ...; // assign the JSON-LD document here function rdfCallback(quads) { console.log("RDF Data: ", quads); }; jsonld.toRDF(jsonldDocument, rdfCallback);
There are currently two other API methods that are in active development and were not ready for publication at the time this document was published. Framing allows a developer to force a different layout for the data and effectively perform query-by-example on JSON-LD documents - this is most useful when a JSON-LD-based REST API does not know the exact form of the data it is getting in, but still wants to operate upon it if some bare essentials are found in the data. JSON-LD normalization allows JSON-LD documents to be deterministically serialized such that they can be digitally signed or be used to find the differences between two linked data graphs. It is expected that framing will be a part of the final API. It is expected that normalization will be an optional feature that JSON-LD processors may implement.
This API provides a clean mechanism that enables developers to convert JSON-LD data into a a variety of output formats that are easier to work with in various programming languages. If a JSON-LD API is provided in a programming environment, the entirety of the following API must be implemented.
The intent of the Working Group and the Editors of this specification is to eventually align terminology used in this document with the terminology used in the RDF Concepts document to the extent to which it makes sense to do so. In general, if there is an analogue to terminology used in this document in the RDF Concepts document, the preference is to use the terminology in the RDF Concepts document.
The following is an explanation of the general terminology used in this document:
@value
,
@list
or @set
and it has one or more keys
other than @id
.@id
key._:
.The JSON-LD processor interface is the high-level programming structure that developers use to access the JSON-LD transformation methods.
The JSON-LD API signatures are the same across all programming languages. Due
to the fact that asynchronous programming is uncommon in certain languages, developers may
implement processor with a synchronous interface instead. In that case, the callback
parameter must not be included and the result must be returned as return value instead.
[NoInterfaceObject]
interface JsonLdProcessor {
void expand (object or object[] or IRI input, object or IRI? context, JsonLdCallback
callback, optional JsonLdOptions
? options);
void compact (object or object[] or IRI input, object or IRI context, JsonLdCallback
callback, optional JsonLdOptions
? options);
void flatten (object or object[] or IRI input, string or IRI graph, object or IRI? context, JsonLdCallback
callback, optional JsonLdOptions
? options);
void fromRDF (Quad
[] input, JsonLdCallback
callback, optional JsonLdOptions
? options);
void toRDF (object or object[] or IRI input, QuadCallback
callback, optional JsonLdOptions
? options);
};
compact
input
using the
context
according to the steps in the
Compaction Algorithm. The
input
must be copied, compacted and returned if there are
no errors. If the compaction fails, an appropriate exception must be
thrown.
@context
.
For example, if a @type
key maps to anything other than
@id
or an absolute IRI, this exception would be raised.@language
value.Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
input | object or object[] or IRI | ✘ | ✘ | The JSON-LD object or array of JSON-LD objects to perform the compaction upon or an IRI referencing the JSON-LD document to compact. |
context | object or IRI | ✘ | ✘ | The context to use when compacting the input ; either in the
form of an JSON object or as IRI. |
callback |
| ✘ | ✘ | A callback that is called when processing is complete on
the given input . |
options |
| ✔ | ✔ | A set of options that may affect the expansion algorithm such as, e.g., the
input document's base IRI. This also includes optimize ,
which if set will cause processor-specific optimization. |
void
expand
input
according to
the steps in the Expansion Algorithm. The
input
must be copied, expanded and returned if there are
no errors. If the expansion fails, an appropriate exception must be thrown.
@context
.
For example, if a @type
key maps to anything other than
@id
or an absolute IRI, this exception would be raised.Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
input | object or object[] or IRI | ✘ | ✘ | The JSON-LD object or array of JSON-LD objects to perform the expansion upon or an IRI referencing the JSON-LD document to expand. |
context | object or IRI | ✔ | ✘ | An optional external context to use additionally to the context embedded in
input when expanding the input . |
callback |
| ✘ | ✘ | A callback that is called when processing is complete on
the given input . |
options |
| ✔ | ✔ | A set of options that may affect the expansion algorithm such as, e.g., the input document's base IRI. |
void
flatten
input
according to
the steps in the Flattening Algorithm. The
input
must be flattened and returned if there are
no errors. If the flattening fails, an appropriate exception must be thrown.
It is still being discussed if the flatten() method should be added or not. See ISSUE-109.
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
input | object or object[] or IRI | ✘ | ✘ | The JSON-LD object or array of JSON-LD objects to flatten or an IRI referencing the JSON-LD document to flatten. |
graph | string or IRI | ✘ | ✘ | The graph in the document that should be flattened. To return the default
graph @default has to be passed, for the merged graph @merged
and for any other graph the IRI identifying the graph has to be passed. The
default value is @merged . |
context | object or IRI | ✔ | ✘ | An optional external context to use additionally to the context embedded in
input when expanding the input . |
callback |
| ✘ | ✘ | A callback that is called when processing is complete on
the given input . |
options |
| ✔ | ✔ | A set of options that may affect the expansion algorithm such as, e.g., the input document's base IRI. |
void
fromRDF
Quads
.
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
input |
| ✘ | ✘ | A dataset represented as an array of Quads . |
callback |
| ✘ | ✘ | A callback that is called when processing is complete on
the given input . |
options |
| ✔ | ✔ | A set of options that will affect the algorithm. This includes notType ,
which if set to true causes the resulting document to use rdf:type
as a property, instead of @type . |
void
toRDF
input
according to the
Convert to RDF Algorithm, calling
the provided callback
for each Quad
generated.
@context
.
For example, if a @type
key maps to anything other than
@id
or an absolute IRI, this exception would be raised.Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
input | object or object[] or IRI | ✘ | ✘ | The JSON-LD object or array of JSON-LD objects to convert to RDF or a IRI
referencing the JSON-LD document to convert to RDF. |
callback |
| ✘ | ✘ | A callback that is called when a Quad is created from processing
the given input .
|
options |
| ✔ | ✔ | A set of options that may affect the conversion to RDF such as, e.g., the input document's base IRI. |
void
The JsonLdCallback
is used to return a processed JSON-LD representation
as the result of processing an API method.
[NoInterfaceObject Callback]
interface JsonLdCallback {
void jsonLd (object or object[] jsonld);
};
jsonLd
Parameter | Type | Nullable | Optional | Description |
---|---|---|---|---|
jsonld | object or object[] | ✘ | ✘ | The processed JSON-LD document. |
void
The QuadCallback
is called whenever the processor generates a
quad during the quad()
call.
[NoInterfaceObject Callback]
interface QuadCallback {
void quad (Quad
quad);
};
This section describes datatype definitions used within the JSON-LD API.
The IRI
datatype is a string representation of an IRI.
typedef DOMString IRI;
The JsonLdOptions
type is used to pass various options to the JsonLdProcessor
methods.
dictionary JsonLdOptions {
IRI
? base;
boolean compactArrays = true;
boolean optimize = false;
boolean useRdfType = false;
boolean useNativeTypes = true;
};
JsonLdOptions
Membersbase
of type IRI
, nullableIRI
. If not specified and input is not
an IRI
, the base IRI defaults to the current document IRI if in a browser context,
or the empty string if there is no document context.
compactArrays
of type boolean, defaulting to true
true
, the JSON-LD processor replaces arrays with just
one element with that element during compaction. If set to false
,
all arrays will remain arrays even if they have just one element.
optimize
of type boolean, defaulting to false
true
, the JSON-LD processor is allowed to
optimize the output of the Compaction Algorithm
to produce even compacter representations. The algorithm for compaction
optimization is beyond the scope of this specification and thus
not defined. Consequently, different implementations may implement
different optimization algorithms.
useNativeTypes
of type boolean, defaulting to true
true
, the JSON-LD processor will try to convert
datatyped literals to JSON native types instead of using the
expanded object form when converting from RDF.
xsd:boolean
values will be converted to true or false.
xsd:integer
and xsd:double
values will be
converted to
JSON numbers.
useRdfType
of type boolean, defaulting to false
true
, the JSON-LD processor will use the
expanded rdf:type
IRI as the property instead of
@type
when converting from RDF.
The following data structures are used for representing data about RDF quads. They are used for normalization, and RDF conversion.
The Quad
interface represents an RDF Quad.
See [RDF-CONCEPTS] definition for
RDF triple,
which most closely aligns to Quad
.
[NoInterfaceObject]
interface Quad {
readonly attribute Node
subject;
readonly attribute Node
property;
readonly attribute Node
object;
readonly attribute Node
? graphName;
};
graphName
of type Node
, readonly, nullableQuad
identifying
it as a member of a named graph. If it is missing, the quad
is a member of the default graph.
object
of type Node
, readonlyQuad
.property
of type Node
, readonlyQuad
.
Within JSON-LD, an RDF predicate is refered to as a
propertysubject
of type Node
, readonlyQuad
.Node
is the base class of IRI
,
BlankNode
, and Literal
. It is the IDL
representation of a linked data graph node.
[NoInterfaceObject]
interface Node {
};
[NoInterfaceObject]
interface IRI : Node
{
readonly attribute DOMString value;
};
value
of type DOMString, readonlyA node in the linked data graph that
does not contain a de-reference-able identifier because it is either
ephemeral in nature or does not contain information that needs to be linked
to from outside of the linked data graph.
A blank node is assigned an identifier starting
with the prefix _:
and an implementation dependent,
auto-generated suffix that is unique to all information associated with the
particular blank node.
[NoInterfaceObject]
interface BlankNode : Node
{
readonly attribute DOMString identifier;
};
identifier
of type DOMString, readonlyidentifier
must not be relied upon in any way between two
separate processing runs of the same document or with a different document.Developers and authors must not assume that the
value of a blank node will remain the same between two
processing runs. BlankNode
values are only valid for the
most recent processing run on the document. BlankNode
values will often be generated differently by different processors.
Implementers must ensure that BlankNode
values are unique
within the current environment, two BlankNodes
are considered equal if, and only if, their values are strictly equal.
Literals represent values such as numbers, dates and strings in
RDF data. A Literal
is comprised of three attributes:
value
language
tagdatatype
specified by an IRI
Literals representing plain text in a natural language may have a
language
tag specified by a string token, as specified in
[BCP47], normalized to lowercase
(e.g., 'en'
, 'fr'
, 'en-gb'
).
They also have a datatype attribute such as xsd:string
.
If unspecified, the datatype
defaults to xsd:string
.
Literals representing values with a specific datatype, such as
the integer 72, may have a datatype
attribute specified in the form
of a IRI
(e.g.,
xsd:integer
).
See[RDF-CONCEPTS] definition for literal.
[NoInterfaceObject]
interface Literal : Node
{
readonly attribute DOMString value;
readonly attribute DOMString? language;
readonly attribute IRI
? datatype;
};
datatype
of type IRI
, readonly, nullablelanguage
of type DOMString, readonly, nullablevalue
of type DOMString, readonlyAll algorithms described in this section are intended to operate on language-native data structures. That is, the serialization to a text-based JSON document isn't required as input or output to any of these algorithms and language-native data structures must be used where applicable.
@context
keyword.
Processing of JSON-LD data structure is managed recursively. During processing, each rule is applied using information provided by the active context. Processing begins by pushing a new processor state onto the processor state stack. If a local context is encountered, information from the local context is merged into the active context.
The active context is used for expanding properties and values of a JSON object (or elements of an array) using a term mapping. It is also used to maintain coercion mappings from terms to datatypes, language mappings from terms to language codes, and list mappings and set mappings for terms. Processors must use the lexical form of the property when creating a mapping, as lookup is performed on lexical forms, not expanded IRI representations.
A local context is identified within a JSON object having a @context
property with a string, array or a JSON object value.
When processing a local context, special processing rules apply:
@context
@context
element using the JSON Pointer "/@context" as described in [JSON-POINTER]. Set context
to the extracted content and process it by starting at Step 2.1.@language
property, it must have a value of a
simple string with the lexical form described in [BCP47], or null. Add the language to the local context.@vocab
key, it must have a value of a simple string
with the lexical form of an absolute IRI, or null. Add the vocabulary mapping to the local context.@id
, @language
or @type
properties, throw an exception.
@type
or @graph
can take a
@container
with @set
.
@id
property with a string value which must have the form of a term,
compact IRI, or absolute IRI. Determine the IRI mapping
by performing IRI Expansion on the associated value.
If the result of the IRI mapping is an absolute IRI, merge the
property into the local context term mapping.@id
property with a string value which must have the
form of a term, compact IRI, or absolute IRI.
Determine the IRI mapping by performing IRI Expansion on the associated
value. If the result of the IRI mapping is an absolute IRI, merge the
property into the local context term mapping.@type
property, its value must have the form of a term,
compact IRI, absolute IRI, or the keyword @id
.
Determine the IRI by performing IRI Expansion on the associated value.
If the result of the IRI mapping is an absolute IRI or @id
, merge into the
local context coercion mapping using the lexical value of the property.@container
property, its value must be @list
or
@set
. Merge the list mapping or set mapping into the
local context using the lexical value of the property.@language
property but no @type
property, the value of the
@language
property must be a string or null.
Merge the language mapping into the local context using the lexical value of the
property.It can be difficult to distinguish between a compact IRI and an absolute IRI, as a compact IRI may seem to be a valid IRI scheme. When performing repeated IRI expansion, a term used as a prefix may not have a valid mapping due to dependencies in resolving term definitions. By continuing Step 2.3.2 until no changes are made, mappings to IRIs created using an undefined term prefix will eventually expand to an absolute IRIs.
Keys and some values are evaluated to produce an IRI. This section defines an algorithm for transforming a value representing an IRI into an actual IRI.
IRIs may be represented as an absolute IRI, a term,
a compact IRI, or as a value relative to @vocab
.
An absolute IRI is defined in [RFC3987] containing a scheme along with path and optional query and fragment segments. A relative IRI is an IRI that is relative some other absolute IRI; in the case of JSON-LD this is the base location of the document.
The algorithm for generating an IRI is:
@type
and the active context has a
@vocab
mapping, join the mapped value to the suffix using textual concatenation.Some keys and values are expressed using IRIs. This section defines an algorithm for transforming an IRI (iri) to a term or compact IRI using the terms specified in the active context using an optional value.
The algorithm for generating a compact IRI is:
0
,
and set a flag list container to false
.@list
:
@container
set to @set
, continue
to the next term.true
and term does not have a
container
set to @list
and value is null,
continue to the next term.container
set to @list
,
continue to the next term.0
:
container
set to @set
, then add
1
to rank.@list
and list container is false
and term has a container
set to @list
, then set list container to true
, clear
terms, set highest rank to rank, and add term to terms.@vocab
which is a prefix of iri where
the resulting relative IRI is not a term in the
active context. The resulting relative IRI is the
unmatched part of iri.@type
and @vocab
is not null and matches the beginning
of iri, return the unmatched portion of iri. Otherwise return
iri.When selecting among multiple possible terms for a given property, it may be that multiple
terms are defined with the same IRI, but differ in @type
, @container
or @language
. The purpose of this algorithm is to take a term
and a value and give it a term rank. The selection can then be based, partly, on
the term having the highest term rank.
Given a term term, value, and active context determine the term rank using the following steps:
3
.@list
:
@value
.
@value
property:
@type
property matching a
@type
coercion for term, term rank
is 3
, otherwise if term has no @type
coercion and no @language
, term rank is
1
, otherwise 0
.@value
is not a string, if term has
no @type
or @language
it is 2
, otherwise 1
.@language
property, if term has
@language
null, or term has no @type
or
@language
and the active context has no @language
,
term rank is 3
, otherwise 0
.@language
property matching a
@language
definition for term (or
term has no @type
or @language
definition and
@language
in the active context matches the
value @language
), term rank is
3
, otherwise if term has no @type
coercion and no @language
, term rank is
1
, otherwise 0
.@type
coerced to @id
,
term rank is 3
, otherwise
if term has no @type
coercion and no @language
,
term rank is 1
, otherwise 0
.Some values in JSON-LD can be expressed in a compact form. These values are required to be expanded at times when processing JSON-LD documents.
The algorithm for expanding a value takes an active property and active context. It is implemented as follows:
@graph
or the target of an @id
coercion,
expand the value into an object with a key-value pair where the key is @id
and the value is
the expanded IRI according to the IRI Expansion rules.@value
and the unexpanded value.@type
and the associated coercion datatype expanded according to the
IRI Expansion rules.@language
and value of the language tagging from the active context.Some values, such as IRIs and typed literals, may be expressed in an expanded form (expanded value) in JSON-LD. These values are required to be compacted at times when processing JSON-LD documents.
The algorithm for compacting an expanded value value takes an active property and active context. It is implemented as follows:
@value
.@graph
, the compacted value is the
value associated with the @id
key, processed according to
the IRI Compaction steps.@id
, the compacted
value is the value associated with the @id
key,
processed according to the
IRI Compaction steps.@value
key.@id
key, the compacted value is value with
the value of @id
processed according to the
IRI Compaction steps.@language
, which
matches the @language
of the value, or the value has only a @value
key, the compacted
value is the value associated with the @value
key.@type
key, the compacted value
is value with the @type
value processed according to the
IRI Compaction steps.Expansion is the process of taking a JSON-LD document and applying a context such that all IRI, datatypes, and literal values are expanded so that the context is no longer necessary. JSON-LD document expansion is typically used as a part of other JSON-LD API methods.
For example, assume the following JSON-LD input document:
{ "@context": { "name": "http://xmlns.com/foaf/0.1/name", "homepage": { "@id": "http://xmlns.com/foaf/0.1/homepage", "@type", "@id" } }, "name": "Manu Sporny", "homepage": "http://manu.sporny.org/" }
Running the JSON-LD Expansion algorithm against the JSON-LD input document provided above would result in the following output:
{ "http://xmlns.com/foaf/0.1/name": "Manu Sporny", "http://xmlns.com/foaf/0.1/homepage": { "@id": "http://manu.sporny.org/" } }
The algorithm takes three input variables: an active context, an active property, and an element to be expanded. To begin, the active context is set to the result of performing, Context Processing on the passed context, or empty if context is null, active property is set to null, and element is set to the JSON-LD input.
@container
set to @list
and any entry in element is an
array, or is a JSON object containing a @list
property,
throw an exception, as lists of lists are not allowed.
If the expanded entry is null, drop it. If it's an array, merge its entries with element's entries.@context
property, update the active context according to
the steps outlined in Context Processing and remove the @context
property.@value
, continue with the next
property from element.@id
the value must be a string.
Expand the value according to IRI Expansion.@type
:
@value
or @language
the value must not be a
JSON object or an array.@list
or @set
expand value
recursively using this algorithm, passing copies of the active context and active property. If the expanded
value is not an array, convert it to an array.
If property is @list
and any entry in value is a
JSON object containing an @list
property, throw an exception, as
lists of lists are not supported.@container
@list
and the expanded value is not null,
convert value to an object with an @list
property whose value is
set to value (unless value is already in that form).@id
, @type
, @value
, or @language
.@value
property
@type
property and its value is not in the form of an
array, convert it to an array.@set
or @list
property, it must be the only property.
Set element to the value of @set
; leave @list
untouched.@language
property, set element to null.If, after the algorithm outlined above is run, the resulting element is an JSON object with just a @graph
property, element is set to the value of @graph
's value. Finally, if element is a JSON object,
it is wrapped into an array.
Compaction is the process of taking a JSON-LD document and applying a context such that the most compact form of the document is generated. JSON is typically expressed in a very compact, key-value format. That is, full IRIs are rarely used as keys. At times, a JSON-LD document may be received that is not in its most compact form. JSON-LD, via the API, provides a way to compact a JSON-LD document.
For example, assume the following JSON-LD input document:
{ "http://xmlns.com/foaf/0.1/name": "Manu Sporny", "http://xmlns.com/foaf/0.1/homepage": { "@id": "http://manu.sporny.org/" } }
Additionally, assume the following developer-supplied JSON-LD context:
{ "@context": { "name": "http://xmlns.com/foaf/0.1/name", "homepage": { "@id": "http://xmlns.com/foaf/0.1/homepage", "@type": "@id" } } }
Running the JSON-LD Compaction algorithm given the context supplied above against the JSON-LD input document provided above would result in the following output:
{ "@context": { "name": "http://xmlns.com/foaf/0.1/name", "homepage": { "@id": "http://xmlns.com/foaf/0.1/homepage", "@type": "@id" } }, "name": "Manu Sporny", "homepage": "http://manu.sporny.org/" }
The compaction algorithm also enables the developer to map any expanded
format into an application-specific compacted format. While the context
provided above mapped http://xmlns.com/foaf/0.1/name
to
name, it could have also mapped it to any arbitrary string
provided by the developer.
The algorithm takes three input variables: an active context, an active property, and an element to be compacted. To begin, the active context is set to the result of performing Context Processing on the passed context, active property is set to null, and element is set to the result of performing the Expansion Algorithm on the JSON-LD input. This removes any existing context to allow the given active context to be cleanly applied.
compactArrays
option is set to
true
, the compacted value is that item; otherwise the compacted value is element.@value
property or element is a node reference,
return the result of performing
Value Compaction on element using active property.@container
mapping to @list
and element has a corresponding @list
property, recursively compact that
property's value passing a copy of the active context and the active property ensuring
that the result is an array with all null values removed. If there already exists a value for
active property in element and the full IRI of property is also coerced to
@list
, throw an exception. Otherwise store the resulting array as value of active property
if empty or property otherwise.@id
or @type
compactArrays
option is set to
true
, value is set to that entry.@container
mapping to @set
or if the
compactArrays
option is set to
false
, convert value to an array.If, after the algorithm outlined above is run, the resulting element is an array, put element into the
@graph
property of a new JSON object and then set element to that JSON object.
Finally, add a @context
property to element and set it to the initially passed context.
Flattening is the process of taking a JSON-LD document, expanding it, labeling all unlabeled nodes with a blank node identifier, and returning an array of the nodes defined in the document.
It is still being discussed if the flatten() method should be added or not. See ISSUE-109.
For example, assume the following JSON-LD input document:
{ "@context": { "name": "http://xmlns.com/foaf/0.1/name", "knows": "http://xmlns.com/foaf/0.1/knows" }, "@id": "http://example.com/markus", "name": "Markus Lanthaler", "knows": { "name": "Manu Sporny" } }
Running the JSON-LD Flattening algorithm for the merged graph (@merged
) against
the JSON-LD input document provided above would result in the following output:
[ { "@id": "http://example.com/markus", "http://xmlns.com/foaf/0.1/knows": [ { "@id": "_:t0" } ], "http://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ] }, { "@id": "_:t0", "http://xmlns.com/foaf/0.1/name": [ { "@value": "Manu Sporny" } ] } ]
The algorithm takes two input variables, an element to flatten and the
graph for which the node definitions should be returned. If graph
is not set, it will default to @merged
which represents the result of
merging all graphs including the default graph (@default
).
The Node Map Generation algorithm takes as input an expanded JSON-LD document and results in a JSON object
nodeMap holding a flat representation of the graphs and nodes represented in the document. All nodes that are not
uniquely identified by an IRI get assigned a (new) blank node identifier. The resulting nodeMap
document will have a property for every graph in the document whose value is another object with a property for every
node represented in the document. While the default graph is stored under the @default
property and the merged graph
under the @merged
property, all other graphs are stored under their respective IRIs.
The algorithm takes as input the expanded JSON-LD document as element, the initially empty nodeMap,
@default
as graph, and null as list.
@value
property,
then if list is not null, append element to list and return.@id
property exists and is an IRI, set id to its value, otherwise
set it to a blank node identifier created by the
Generate Blank Node Identifier algorithm.@id
.@id
. Let node be the value of id in nodes.@id
and each value in element ordered
by property:
@graph
, recursively call this algorithm passing value
for element, nodeMap, null for list and if graph
is @merged
use graph, otherwise use id for graph and then continue.@type
and is a keyword, merge property
and
value
into node
and then continue.@id
is not an IRI or it does not exist,
map v to a new blank node identifier
to avoid collisions. If one does not already exist, add a node reference for
v into node for property.@list
then recursively call this algorithm
with the value of @list
as element, nodeMap, graph, and
a new array flattenedList as list. Create a new JSON object with the
property @list
set to flattenedList and add it to node for
property.@type
and v is not an IRI,
generate a new blank node identifier and add it
to node for property.After the above outlined algorithm has been executed, the node map for all graphs including the default graph are contained in
nodeMap. To also create the node map for the merged graph, execute the algorithm again, but pass @merged
for graph.
This algorithm is used by the Node Map Generation Algorithm to deterministically name blank node identifiers. It uses a identifier map and prefix and takes a possibly null identifier and returns a new identifier based on prefix.
The variable next identifier is initialized to prefix appended with 0
. The
default value of prefix is _:t
.
A JSON-LD document may be converted between other RDF-compatible document formats using the algorithms specified in this section.
The JSON-LD Processing Model describes processing rules for extracting RDF
from a JSON-LD document, and for transforming an array of Quad
retrieved by processing
another serialization format into JSON-LD. Note that many uses of JSON-LD may not require
generation of RDF.
The processing algorithms described in this section are provided in order to demonstrate how one might implement a JSON-LD to RDF processor. Conformant implementations are only required to produce the same type and number of quads during the output process and are not required to implement the algorithm exactly as described.
This section is non-normative.
JSON-LD is intended to have an easy to parse grammar that closely models existing practice in using JSON for describing object representations. This allows the use of existing libraries for parsing JSON.
As with other grammars used for describing Linked Data, a key
concept is that of a node in a linked data graph.
Nodes may be of three basic types.
The first is the IRI
, which is used to refer to
nodes in other linked data graphs.
The second is the
blank node, which are nodes for which an external name does not
exist, or is not known. The third is a Literal, which express
values such as strings, dates and other information having a lexical
form, possibly including an explicit language or datatype.
Data described with JSON-LD may be considered to be a graph made up of subject and object nodes related via a property node. Specific implementations may also choose to operate on the document as a normal JSON description of objects having attributes. Both approaches are valid ways to interact with JSON-LD documents.
This section is non-normative.
The following examples show simple transformations of JSON-LD documents to Turtle [TURTLE-TR].
The first example uses a simple document containing a simple FOAF profile:
{ "@context": {"foaf": "http://xmlns.com/foaf/0.1/"}, "@id": "http://greggkellogg.net/foaf#me", "@type": "foaf:Person", "foaf:name": "Gregg Kellogg", "foaf:knows": { "@type": "foaf:Person", "foaf:name": "Manu Sporny" } }
This translates fairly directly to a similar Turtle document:
@prefix foaf: <http://xmlns.com/foaf/0.1/>. <http://greggkellogg.net/foaf#me> a foaf:Person; foaf:name "Gregg Kellogg"; foaf:knows [ a foaf:Person; foaf:name "Manu Sporny"].
The actual parsing steps first require that the JSON-LD document be expanded,
to eliminate the @context
:
[{ "@id": "http://greggkellogg.net/foaf#me", "@type": ["http://xmlns.com/foaf/0.1/Person"], "http://xmlns.com/foaf/0.1/name": [{"@value": "Gregg Kellogg"}], "http://xmlns.com/foaf/0.1/knows": [{ "@type": ["http://xmlns.com/foaf/0.1/Person"], "http://xmlns.com/foaf/0.1/name": [{"@value": "Manu Sporny"}] }] }]
The process of translating this to RDF then operates over each
node definition to find a subject,
each property to find an RDF predicate,
and each value of that property to find an object.
In this case, each property has just a single object:
foaf:name
identifies a literal, and
foaf:knows
identifies a second node definition
similar to Turtle's blankNodePropertyList
.
After expansion, JSON-LD numbers, booleans, typed literals, language-tagged-strings, and IRIs become explicit, and can be directly transformed into their RDF representations.
[{
"@id": "http://greggkellogg.net/foaf#me",
"@type": ["http://xmlns.com/foaf/0.1/Person"],
"http://xmlns.com/foaf/0.1/name": [{"@value": "Gregg Kellogg"}],
"http://xmlns.com/foaf/0.1/currentProject": [{"@id": "http://json-ld.org/"}],
"http://xmlns.com/foaf/0.1/birthday": [{
"@value": "1957-02-27",
"@type": "http://www.w3.org/2001/XMLSchema#date"
}],
"http://xmlns.com/foaf/0.1/knows": [{
"@type": ["http://xmlns.com/foaf/0.1/Person"],
"http://xmlns.com/foaf/0.1/name": [{"@value": "Manu Sporny"}]
}]
}]
Translates to:
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
<http://greggkellogg.net/foaf#me> a foaf:Person;
foaf:name "Gregg Kellogg";
foaf:currentProject <http://json-ld.org/>;
foaf:birthday "1957-02-27"^^xsd:date;
foaf:knows [ a foaf:Person; foaf:name "Manu Sporny"].
The algorithm below is designed for in-memory implementations with random access to JSON object elements.
A conforming JSON-LD processor implementing RDF conversion must implement a processing algorithm that results in the same set of RDF Quads that the following algorithm generates:
The algorithm takes five input variables: a element to be converted, an active subject, active property and graph name. To begin, the active subject, active property and graph name are set to null, and element is set to the result of performing the Expansion Algorithm on the JSON-LD input. This removes any existing context to allow the given context to be cleanly applied.
@value
property:
@value
is a number, set the
active object to a typed literal using a string representation
of the value as defined in the section Data Round Tripping.
Set datatype to the value of the @type
property if it exists, otherwise
either xsd:integer
or xsd:double
, depending
on if the value contains a fractional and/or an exponential component.
@value
is true or false,
set the active object to a typed literal created from the
string representation of the value. Set datatype to the value of the @type
property if it exists, otherwise xsd:boolean
.@type
property, set the
active object to a typed literal.
@language
property, set the
active object to a language-tagged string.
xsd:string
as the datatype.
@list
property the value must be an array.
Process its value as a list as described in List Conversion using
the return value as the active object
Quad
representing active subject, active property,
active object, and graph name.@id
property,
the value must be a string, set the active subject to the previously
expanded value (either a blank node or an IRI
).@id
property, set the active
subject to newly generated blank node.@type
, set the active property
to rdf:type
.
@graph
,
process value algorithm recursively, using active subject as graph name
and null values for active subject and active property and then
proceed to next property.rdf:type
so set the active object to an IRI
.Quad
using
active subject,active property, active object and
graph name.
List Conversion is the process of taking an array of values and adding them to a newly
created RDF Collection (see
[RDF-SCHEMA]) by linking each element of the list using rdf:first
and rdf:next
,
terminating the list with rdf:nil
using the following sequence:
The algorithm is invoked with an array array, the active property and returns a value to be used as an active object in the calling location.
rdf:nil
.
Quad
using using the active subject, active property
and a newly generated blank node identified as first blank node.
rdf:first
as the active property.
rdf:nil
.Quad
using first blank node,
rdf:rest
and rest blank node.In some cases, data exists natively in Triples or Quads form; for example, if the data was originally
represented in an RDF graph or triple/quad store. This algorithm is designed to simply translate
an array of Quads
into a JSON-LD document.
When expanding typed literal values having a datatype of xsd:string
,
the @type
must not be set to xsd:string
and the resulting value
must have only a @value
property.
The conversion algorithm takes a single parameter input in the form of an
array of Quad
representations.
rdf:first
,
use the entry in graph.listMap indexed by subject,
initializing it to a new JSON object if nesessary. Represent
object in expanded form, as described in
Value Expansion. Add the
resulting object representation to the entry indexed by
first, and skip to the next quad.rdf:rest
:
@id
and
a string representation of name.@id
and
a string representation of subject if necessary.rdf:type
and the
useRdfType
option is not present or false:
@type
, creating an entry in value if necessary.useNativeTypes
option is set to true:
xsd:boolean
, the
converted value is true if the literal
matches the value true
or false if
the literal matches the value false
.xsd:integer
or
xsd:double
, try to convert the literal to a
JSON number. If the conversion is successful,
store the result in converted value, otherwise
set converted value to value.rdf:nil
:
@list
representation to the array value for
key, creating an entry in value if necessary.@id
in value.@list
initialized to a new array
containing the value of first from entry.@graph
in entry containing the ordered entries
from graphs[subject].nodes.When converting JSON-LD to RDF JSON-native types such as numbers and booleans are automatically coerced to xsd:integer, xsd:double, or xsd:boolean. Implementers must ensure that the result is a canonical lexical form in the form of a string. A canonical lexical form is a set of literals from among the valid set of literals for a datatype such that there is a one-to-one mapping between the canonical lexical form and a value in the value space as defined in [XMLSCHEMA11-2]. In other words, every value must be converted to a deterministic string representation.
The canonical lexical form of an integer, i.e., a number without fractions
or a number coerced to xsd:integer, is a finite-length sequence of decimal
digits (0-9
) with an optional leading minus sign; leading zeroes are prohibited.
To convert the number in JavaScript, implementers can use the following snippet of code:
(value).toFixed(0).toString()
The canonical lexical form of a double, i.e., a number with fractions
or a number coerced to xsd:double, consists of a mantissa followed by the
character "E", followed by an exponent. The mantissa must be a decimal number. The exponent
must be an integer. Leading zeroes and a preceding plus sign (+
) are prohibited
in the exponent. If the exponent is zero, it must be indicated by E0
.
For the mantissa, the preceding optional plus sign is prohibited and the decimal point is
required. Leading and trailing zeroes are prohibited subject to the following: number
representations must be normalized such that there is a single digit which is non-zero to the
left of the decimal point and at least a single digit to the right of the decimal point unless
the value being represented is zero. The canonical representation for zero is 0.0E0
.
xsd:double's value space is defined by the IEEE double-precision 64-bit
floating point type [IEEE-754-1985]; in JSON-LD the mantissa is rounded to 15 digits after the
decimal point.
To convert the number in JavaScript, implementers can use the following snippet of code:
(value).toExponential(15).replace(/(\d)0*e\+?/,'$1E')
When data such as decimals need to be normalized, JSON-LD authors should not use values that are going to undergo automatic conversion. This is due to the lossy nature of xsd:double values. Authors should instead use the expanded object form to set the canonical lexical form directly.
The canonical lexical form of the boolean values true
and false
are the strings true and false.
When JSON-native numbers, are type coerced, lossless data round-tripping can not be guaranted as rounding errors might occur. Additionally, only literals typed as xsd:integer, xsd:double, and xsd:boolean are automatically converted back to their JSON-native counterparts in when converting from RDF.
It is being discussed if the automatic mapping of JSON-native types to the XSD type system should be configurable or not.
Some JSON serializers, such as PHP's native implementation in some versions,
backslash-escape the forward slash character. For example, the value
http://example.com/
would be serialized as http:\/\/example.com\/
.
This is problematic as other JSON parsers might not understand those escaping characters.
There is no need to backslash-escape forward slashes in JSON-LD. To aid interoperability
between JSON-LD processors, a JSON-LD serializer must not backslash-escape forward slashes.
A large amount of thanks goes out to the JSON-LD Community Group participants who worked through many of the technical issues on the mailing list and the weekly telecons - of special mention are Niklas Lindström, François Daoust, and Zdenko 'Denny' Vrandečić. The editors would like to thank Mark Birbeck, who provided a great deal of the initial push behind the JSON-LD work via his work on RDFj. The work of Dave Lehn and Mike Johnson are appreciated for reviewing, and performing several implementations of the specification. Ian Davis is thanked for this work on RDF/JSON. Thanks also to Nathan Rixham, Bradley P. Allen, Kingsley Idehen, Glenn McDonald, Alexandre Passant, Danny Ayers, Ted Thibodeau Jr., Olivier Grisel, Josh Mandel, Eric Prud'hommeaux, David Wood, Guus Schreiber, Pat Hayes, Sandro Hawke, and Richard Cyganiak for their input on the specification.