CARVIEW |
JSON-LD 1.1 Processing Algorithms and API
Final Community Group Report
- Latest editor's draft:
- https://json-ld.org/spec/latest/json-ld-api/
- Test suite:
- https://json-ld.org/test-suite/
- Previous version:
- https://www.w3.org/TR/2014/REC-json-ld-api-20140116/
- Editor:
- Gregg Kellogg (Spec-Ops) (v1.0 and v1.1)
- Former editors:
- Markus Lanthaler (Graz University of Technology) (v1.0)
- Manu Sporny (Digital Bazaar) (v1.0)
- Authors:
- Dave Longley (Digital Bazaar) (v1.0)
- Gregg Kellogg (Spec-Ops) (v1.0 and v1.1)
- Markus Lanthaler (Graz University of Technology) (v1.0)
- Manu Sporny (Digital Bazaar) (v1.0)
- Niklas Lindström (v1.0)
- Participate:
- GitHub json-ld/json-ld.org
- File a bug
- Commit history
- Pull requests
Copyright © 2010-2018 the Contributors to the JSON-LD 1.1 Processing Algorithms and API Specification, published by the JSON for Linking Data W3C Community Group under the W3C Community Final Specification Agreement (FSA). A human-readable summary is available.
Abstract
This specification defines a set of algorithms for programmatic transformations of JSON-LD documents. Restructuring data according to the defined transformations often dramatically simplifies its usage. Furthermore, this document proposes an Application Programming Interface (API) for developers implementing the specified algorithms.
Status of This Document
This specification was published by the JSON for Linking Data W3C Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Final Specification Agreement (FSA) other conditions apply. Learn more about W3C Community and Business Groups.
This document has been developed by the JSON for Linking Data W3C Community Group as an update to the 1.0 recommendation [JSON-LD-API] developed by the RDF Working Group. The specification has undergone significant development, review, and changes during the course of several years.
There are several independent interoperable implementations of this specification, a test suite [JSON-LD-TESTS] and a live JSON-LD playground that is capable of demonstrating the features described in this document.
If you wish to make comments regarding this document, please send them to public-linked-json@w3.org (subscribe, archives).
Set of Documents
This document is one of three JSON-LD 1.1 Recommendations produced by the JSON for Linking Data W3C Community Group:
1. Introduction §
This section is non-normative.
This document is a detailed specification of the JSON-LD processing algorithms. The document is primarily intended for the following audiences:
- Software developers who want to implement the algorithms to transform JSON-LD documents.
- Web authors and developers who want a very detailed view of how a JSON-LD Processor operates.
- Developers who want an overview of the proposed JSON-LD API.
To understand the basics in this specification you must first be familiar with JSON, which is detailed in [RFC7159]. You must also understand the JSON-LD syntax defined in the JSON-LD 1.1 Syntax specification [JSON-LD11CG], 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 [ECMASCRIPT-6.0] and WebIDL [WEBIDL]. To understand how JSON-LD maps to RDF, it is helpful to be familiar with the basic RDF concepts [RDF11-CONCEPTS].
1.1 Contributing §
There are a number of ways that one may participate in the development of this specification:
- Technical discussion typically occurs on the public mailing list: public-linked-json@w3.org
- The #json-ld IRC channel is available for real-time discussion on irc.freenode.net.
1.2 Terminology §
This document uses the following terms as defined in JSON [RFC7159]. Refer to the JSON Grammar section in [RFC7159] for formal definitions.
- array
- In the JSON serialization, an array structure is represented as square brackets surrounding zero or more values. Values are separated by commas. In the internal representation, an array is an ordered collection of zero or more values. While JSON-LD uses the same array representation as JSON, the collection is unordered by default. While order is preserved in regular JSON arrays, it is not in regular JSON-LD arrays unless specifically defined (see Sets and Lists in the JSON-LD Syntax specification [JSON-LD11CG]).
- JSON object
- In the JSON serialization, an object structure is represented as a pair of curly brackets surrounding zero or more key-value pairs. A key is a string. A single colon comes after each key, separating the key from the value. A single comma separates a value from a following key. In JSON-LD the keys in an object MUST be unique. In the internal representation a JSON object is equivalent to a dictionary (see [WEBIDL]).
- JSON-LD internal representation
- The JSON-LD internal representation is the result of transforming a JSON syntactic structure into the core data structures suitable for direct processing: arrays, dictionaries, strings, numbers, booleans, and null.
- null
-
The use of the null value within JSON-LD is used to
ignore or reset values. A key-value pair in the
@context
where the value, or the@id
of the value, isnull
explicitly decouples a term's association with an IRI. A key-value pair in the body of a JSON-LD document whose value isnull
has the same meaning as if the key-value pair was not defined. If@value
,@list
, or@set
is set tonull
in expanded form, then the entire JSON object is ignored. - number
- In the JSON serialization, a number is similar to that used in most programming languages, except that the octal and hexadecimal formats are not used and that leading zeros are not allowed. In the internal representation, a number is equivalent to either a long or double, depending on if the number has a non-zero fractional part (see [WEBIDL]).
- scalar
- A scalar is either a JSON string, number, true, or false.
- string
- A string is a sequence of zero or more Unicode (UTF-8) characters, wrapped in double quotes, using backslash escapes (if necessary). A character is represented as a single character string.
- true and false
- Values that are used to express one of two possible boolean states.
Furthermore, the following terminology is used throughout this document:
- absolute IRI
- An absolute IRI is defined in [RFC3987] containing a scheme along with a path and optional query and fragment segments.
- active context
- A context that is used to resolve terms while the processing algorithm is running.
- base IRI
- The base IRI is an absolute IRI established in the context, or is based on the JSON-LD document location. The base IRI is used to turn relative IRIs into absolute IRIs.
- blank node
-
A node in a graph that is neither an
IRI, nor a JSON-LD value, nor a list.
A blank node does not contain a de-referenceable
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
_:
. - blank node identifier
-
A blank node identifier is a string that can be used as an identifier for a
blank node within the scope of a JSON-LD document. Blank node identifiers
begin with
_:
. - compact IRI
- A compact IRI is has the form of prefix:suffix and is used as a way of expressing an IRI without needing to define separate term definitions for each IRI contained within a common vocabulary identified by prefix.
- context
- A a set of rules for interpreting a JSON-LD document as specified in The Context of the JSON-LD Syntax specification [JSON-LD11CG].
- datatype IRI
- A datatype IRI as specified by [RDF11-CONCEPTS].
- default graph
- The default graph is the only graph in a JSON-LD document which has no graph name. When executing an algorithm, the graph where data should be placed if a named graph is not specified.
- default language
-
The default language is set in the context using the
@language
key whose value MUST be a string representing a [BCP47] language code ornull
. - edge
- Every edge has a direction associated with it and is labeled with an IRI or a blank node identifier. Within the JSON-LD syntax these edge labels are called properties. Whenever possible, an edge should be labeled with an IRI.
- expanded term definition
- An expanded term definition, is a term definition where the value is a JSON object containing one or more keyword properties to define the associated absolute IRI, if this is a reverse property, the type associated with string values, and a container mapping.
- Frame
- A JSON-LD document, which describes the form for transforming another JSON-LD document using matching and embedding rules. A frame document allows additional keywords and certain property values to describe the matching and transforming process.
- graph name
- The IRI identifying a named graph.
- graph object
-
A graph object represents a named graph represented as the
value of a property within a node object. When expanded, a
graph object MUST have an
@graph
member, and may also have@id
, and@index
members. A simple graph object is a graph object which does not have an@id
member. Note that node objects may have a@graph
member, but are not considered graph objects if they include any other properties. A top-level object consisting of@graph
is also not a graph object. - id map
-
An id map is a JSON object value of a term defined with
@container
set to@id
, who's keys are interpreted as IRIs representing the@id
of the associated node object; value MUST be a node object. If the value contains a property expanding to@id
, it's value MUST be equivalent to the referencing key. - index map
-
An index map is a JSON object value of a term defined with
@container
set to@index
, whose values MUST be any of the following types: string, number, true, false, null, node object, value object, list object, set object, or an array of zero or more of the above possibilities. - IRI
- An Internationalized Resource Identifier as described in [RFC3987].
- JSON-LD document
- A JSON-LD document is a serialization of a collection of graphs and comprises exactly one default graph and zero or more named graphs.
- JSON-LD Processor
- A JSON-LD Processor is a system which can perform the algorithms defined in [JSON-LD11CG-API].
- JSON-LD value
- A JSON-LD value is a string, a number, true or false, a typed value, or a language-tagged string.
- keyword
- A JSON key that is specific to JSON-LD, specified in the JSON-LD Syntax specification [JSON-LD11CG] in the section titled Syntax Tokens and Keywords.
- language map
-
An language map is a JSON object value of a term defined with
@container
set to@language
, whose keys MUST be strings representing [BCP47] language codes and the values MUST be any of the following types: null, string, or an array of zero or more of the above possibilities. - language-tagged string
- A language-tagged string consists of a string and a non-empty language tag as defined by [BCP47]. The language tag MUST be well-formed according to section 2.2.9 Classes of Conformance of [BCP47], and is normalized to lowercase.
- linked data graph
- A labeled directed graph, i.e., a set of nodes connected by edges, as specified in the Data Model section of the JSON-LD specification [JSON-LD11CG]. A linked data graph is a generalized representation of an RDF graph as defined in [RDF-CONCEPTS].
- list
- A list is an ordered sequence of IRIs, blank nodes, and JSON-LD values. See RDF collection in [RDF-SCHEMA].
- list object
-
A list object is a JSON object that has an
@list
member. - local context
-
A context that is specified within a JSON object,
specified via the
@context
keyword. - named graph
- A named graph is a linked data graph that is identified by an IRI or blank node.
- node
- Every node is an IRI, a blank node, a JSON-LD value, or a list. A piece of information that is represented in a linked data graph.
- node object
-
A node object represents zero or more properties of a
node in the graph serialized by the
JSON-LD document. A JSON object is a node object
if it exists outside of the JSON-LD context and:
- it does not contain the
@value
,@list
, or@set
keywords, or - it is not the top-most JSON object in the JSON-LD document consisting
of no other members than
@graph
and@context
.
- it does not contain the
- object
- An object is a node in a linked data graph with at least one incoming edge. See RDF objectin [RDF11-CONCEPTS].
- prefix
- A prefix is the first component of a compact IRI which comes from a term that maps to a string that, when prepended to the suffix of the compact IRI results in an absolute IRI.
- processing mode
-
The processing mode defines how a JSON-LD document is processed.
By default, all documents are assumed to be conformant with
JSON-LD 1.0 [JSON-LD]. By defining
a different version using the
@version
member in a context, or via explicit API option, other processing modes can be accessed. This specification defines extensions for thejson-ld-1.1
processing mode. - property
- The IRI label of an edge in a linked data graph. See RDF predicate in [RDF11-CONCEPTS].
- RDF dataset
- A dataset as specified by [RDF11-CONCEPTS] representing a collection of RDF graphs.
- RDF resource
- A resource as specified by [RDF11-CONCEPTS].
- RDF triple
- A triple as specified by [RDF11-CONCEPTS].
- relative IRI
-
A relative IRI is an IRI that is relative to some other absolute IRI,
typically the base IRI of the document. Note that
properties, values of
@type
, and values of terms defined to be vocabulary relative are resolved relative to the vocabulary mapping, not the base IRI. - set object
-
A set object is a JSON object that has an
@set
member. - subject
- A subject is anode in a linked data graph with at least one outgoing edge, related to an object node through a property. See RDF subject in [RDF11-CONCEPTS].
- term
- A term is a short word defined in a context that MAY be expanded to an IRI
- term definition
- A term definition is an entry in a context, where the key defines a term which may be used within a JSON object as a property, type, or elsewhere that a string is interpreted as a vocabulary item. Its value is either a string (simple term definition), expanding to an absolute IRI, or an expanded term definition.
- type map
-
An type map is a JSON object value of a term defined with
@container
set to@type
, who's keys are interpreted as IRIs representing the@type
of the associated node object; value MUST be a node object, or array of node objects. If the value contains a property expanding to@type
, it's values are merged with the map value when expanding. - typed value
- A typed value consists of a value, which is a string, and a type, which is an IRI.
- value object
-
A value object is a JSON object that has an
@value
member. - vocabulary mapping
-
The vocabulary mapping is set in the context using the
@vocab
key whose value MUST be an absolute IRInull
.
1.2.1 Algorithm Terms §
The Following terms are used within specific algorithms.
- active graph
- The name of the currently active graph that the processor should use when processing.
- active property
- The currently active property or keyword that the processor should use when processing. The active property is represented in the original lexical form, which is used for finding coercion mappings in the active context.
- JSON-LD input
- The JSON-LD data structure that is provided as input to the algorithm.
- promise
- A promise is an object that represents the eventual result of a single asynchronous operation. Promises are defined in [ECMASCRIPT-6.0].
1.3 Typographical conventions §
The following typographic conventions are used in this specification:
markup
- Markup (elements, attributes, properties), machine processable values (string, characters, media types), property name, or a file name is in red-orange monospace font.
- variable
- A variable in pseudo-code or in an algorithm description is in italics.
- definition
- A definition of a term, to be used elsewhere in this or other specifications, is in bold and italics.
- definition reference
- A reference to a definition in this document is underlined and is also an active link to the definition itself.
markup definition reference
- A references to a definition in this document, when the reference itself is also a markup, is underlined, red-orange monospace font, and is also an active link to the definition itself.
- external definition reference
- A reference to a definition in another document is underlined, in italics, and is also an active link to the definition itself.
markup external definition reference
- A reference to a definition in another document, when the reference itself is also a markup, is underlined, in italics red-orange monospace font, and is also an active link to the definition itself.
- hyperlink
- A hyperlink is underlined and in blue.
- [reference]
- A document reference (normative or informative) is enclosed in square brackets and links to the references section.
- Changes from Recommendation
- Sections or phrases changed from the previous Recommendation are highlighted.
Notes are in light green boxes with a green left border and with a "Note" header in green. Notes are normative or informative depending on the whether they are in a normative or informative section, respectively.
Examples are in light khaki boxes, with khaki left border, and with a
numbered "Example" header in khaki. Examples are always informative.
The content of the example is in monospace font and may be syntax colored.
1.4 Example Conventions §
Note that in the examples used in this document, output is of necessity shown in serialized form as JSON. While the algorithms describe operations on the JSON-LD internal representation, when they as displayed as examples, the JSON serialization is used. In particular, the internal representation use of dictionaries are represented using JSON objects.
{ "@context": { "name": "https://xmlns.com/foaf/0.1/name", "knows": "https://xmlns.com/foaf/0.1/knows" }, "@id": "https://me.markus-lanthaler.com/", "name": "Markus Lanthaler", "knows": [ { "name": "Dave Longley" } ] }
In the internal representation, the example above would be of a
dictionary containing @context
, @id
, name
, and knows
keys,
with either dictionaries, strings, or arrays of
dictionaries or strings values. In the JSON serialization, JSON objects are used
for dictionaries, while arrays and strings are serialized using a
convention common to many programming languages.
2. Features §
This section is non-normative.
The JSON-LD 1.1 Syntax specification [JSON-LD11CG] defines a syntax to express Linked Data in JSON. Because there is more than one way to express Linked Data using this syntax, it is often useful to be able to transform JSON-LD documents so that they may be more easily consumed by specific applications.
To allow these algorithms to be adapted for syntaxes other than JSON, the algorithms operate on the JSON-LD internal representation, which uses the generic concepts of arrays, dictionaries, strings, numbers, booleans, and null to describe the data represented by a JSON document. Algorithms act on this internal representation with API entry points responsible for transforming between the concrete and internal representations.
JSON-LD uses contexts to allow Linked Data to be expressed in a way that is specifically tailored to a particular person or application. By providing a context, JSON data can be expressed in a way that is a natural fit for a particular person or application whilst also indicating how the data should be understood at a global scale. In order for people or applications to share data that was created using a context that is different from their own, a JSON-LD processor must be able to transform a document from one context to another. Instead of requiring JSON-LD processors to write specific code for every imaginable context switching scenario, it is much easier to specify a single algorithm that can remove any context. Similarly, another algorithm can be specified to subsequently apply any context. These two algorithms represent the most basic transformations of JSON-LD documents. They are referred to as expansion and compaction, respectively.
JSON-LD 1.1 introduces new features that are
compatible with JSON-LD 1.0 [JSON-LD],
but if processed by a JSON-LD 1.0 processor may produce different results.
In order to detect this JSON-LD 1.1 requires that the processing
mode be explicitly set to json-ld-1.1
, either through the
processingMode
API option, or using the
@version
member within a context.
There are four major types of transformation that are discussed in this document: expansion, compaction, flattening, and RDF serialization/deserialization.
2.1 Expansion §
This section is non-normative.
The algorithm that removes context is called expansion. Before performing any other transformations on a JSON-LD document, it is easiest to remove any context from it and to make data structures more regular.
To get an idea of how context and data structuring affects the same data, here is an example of JSON-LD that uses only terms and is fairly compact:
{ "@context": { "name": "https://xmlns.com/foaf/0.1/name", "homepage": { "@id": "https://xmlns.com/foaf/0.1/homepage", "@type": "@id" } }, "@id": "https://me.markus-lanthaler.com/", "name": "Markus Lanthaler", "homepage": "https://www.markus-lanthaler.com/" }
The next input example uses one IRI to express a property and an array to encapsulate another, but leaves the rest of the information untouched.
{ "@context": { "website": "https://xmlns.com/foaf/0.1/homepage" }, "@id": "https://me.markus-lanthaler.com/", "https://xmlns.com/foaf/0.1/name": "Markus Lanthaler", "website": { "@id": "https://www.markus-lanthaler.com/" } }
Note that both inputs are valid JSON-LD and both represent the same information. The difference is in their context information and in the data structures used. A JSON-LD processor can remove context and ensure that the data is more regular by employing expansion.
Expansion has two important goals: removing any contextual
information from the document, and ensuring all values are represented
in a regular form. These goals are accomplished by expanding all properties
to absolute IRIs and by expressing all
values in arrays in
expanded form. Expanded form is the most verbose
and regular way of expressing of values in JSON-LD; all contextual
information from the document is instead stored locally with each value.
Running the Expansion algorithm
(expand
)
operation) against the above examples results in the following output:
[ { "@id": "https://me.markus-lanthaler.com/", "https://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ], "https://xmlns.com/foaf/0.1/homepage": [ { "@id": "https://www.markus-lanthaler.com/" } ] } ]
The example above is the JSON-LD serialization of the output of the expansion algorithm, where the algorithm's use of dictionaries are replaced with JSON objects.
Note that in the output above all context definitions have been removed, all terms and compact IRIs have been expanded to absolute IRIs, and all JSON-LD values are expressed in arrays in expanded form. While the output is more verbose and difficult for a human to read, it establishes a baseline that makes JSON-LD processing easier because of its very regular structure.
2.2 Compaction §
This section is non-normative.
While expansion removes context from a given input, compaction's primary function is to perform the opposite operation: to express a given input according to a particular context. Compaction applies a context that specifically tailors the way information is expressed for a particular person or application. This simplifies applications that consume JSON or JSON-LD by expressing the data in application-specific terms, and it makes the data easier to read by humans.
Compaction uses a developer-supplied context to shorten IRIs to terms or compact IRIs and JSON-LD values expressed in expanded form to simple values such as strings or numbers.
For example, assume the following expanded JSON-LD input document:
[ { "@id": "https://me.markus-lanthaler.com/", "https://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ], "https://xmlns.com/foaf/0.1/homepage": [ { "@id": "https://www.markus-lanthaler.com/" } ] } ]
Additionally, assume the following developer-supplied JSON-LD context:
{ "@context": { "name": "https://xmlns.com/foaf/0.1/name", "homepage": { "@id": "https://xmlns.com/foaf/0.1/homepage", "@type": "@id" } } }
Running the Compaction Algorithm
(compact
)
operation) given the context supplied above against the JSON-LD input
document provided above would result in the following output:
{ "@context": { "name": "https://xmlns.com/foaf/0.1/name", "homepage": { "@id": "https://xmlns.com/foaf/0.1/homepage", "@type": "@id" } }, "@id": "https://me.markus-lanthaler.com/", "name": "Markus Lanthaler", "homepage": "https://www.markus-lanthaler.com/" }
The example above is the JSON-LD serialization of the output of the compaction algorithm, where the algorithm's use of dictionaries are replaced with JSON objects.
Note that all IRIs have been compacted to
terms as specified in the context,
which has been injected into the output. While compacted output is
useful to humans, it is also used to generate structures that are easy to
program against. Compaction enables developers to map any expanded document
into an application-specific compacted document. While the context provided
above mapped https://xmlns.com/foaf/0.1/name
to name
, it
could also have been mapped to any other term provided by the developer.
2.3 Flattening §
This section is non-normative.
While expansion ensures that a document is in a uniform structure, flattening goes a step further to ensure that the shape of the data is deterministic. In expanded documents, the properties of a single node may be spread across a number of different dictionaries. By flattening a document, all properties of a node are collected in a single dictionary and all blank nodes are labeled with a blank node identifier. This may drastically simplify the code required to process JSON-LD data in certain applications.
For example, assume the following JSON-LD input document:
{ "@context": { "name": "https://xmlns.com/foaf/0.1/name", "knows": "https://xmlns.com/foaf/0.1/knows" }, "@id": "https://me.markus-lanthaler.com/", "name": "Markus Lanthaler", "knows": [ { "name": "Dave Longley" } ] }
Running the Flattening Algorithm
(flatten
)
operation) with a context set to null to prevent compaction
returns the following document:
[ { "@id": "_:t0", "https://xmlns.com/foaf/0.1/name": [ { "@value": "Dave Longley" } ] }, { "@id": "https://me.markus-lanthaler.com/", "https://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ], "https://xmlns.com/foaf/0.1/knows": [ { "@id": "_:t0" } ] } ]
The example above is the JSON-LD serialization of the output of the flattening algorithm, where the algorithm's use of dictionaries are replaced with JSON objects.
Note how in the output above all properties of a node are collected in a
single dictionary and how the blank node representing
"Dave Longley" has been assigned the blank node identifier
_:t0
.
To make it easier for humans to read or for certain applications to process it, a flattened document can be compacted by passing a context. Using the same context as the input document, the flattened and compacted document looks as follows:
{ "@context": { "name": "https://xmlns.com/foaf/0.1/name", "knows": "https://xmlns.com/foaf/0.1/knows" }, "@graph": [ { "@id": "_:t0", "name": "Dave Longley" }, { "@id": "https://me.markus-lanthaler.com/", "name": "Markus Lanthaler", "knows": { "@id": "_:t0" } } ] }
Please note that the result of flattening and compacting a document
is always a dictionary,
(represented as a JSON object when serialized),
which contains an @graph
member that represents the default graph.
2.4 RDF Serialization/Deserialization §
This section is non-normative.
JSON-LD can be used to serialize RDF data as described in [RDF11-CONCEPTS]. This ensures that data can be round-tripped to and from any RDF syntax without any loss in fidelity.
For example, assume the following RDF input serialized in Turtle [TURTLE]:
<https://me.markus-lanthaler.com/> <https://xmlns.com/foaf/0.1/name> "Markus Lanthaler" . <https://me.markus-lanthaler.com/> <https://xmlns.com/foaf/0.1/homepage> <https://www.markus-lanthaler.com/> .
Using the Serialize RDF as JSON-LD algorithm a developer could transform this document into expanded JSON-LD:
[ { "@id": "https://me.markus-lanthaler.com/", "https://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ], "https://xmlns.com/foaf/0.1/homepage": [ { "@id": "https://www.markus-lanthaler.com/" } ] } ]
The example above is the JSON-LD serialization of the output of the Serialize RDF as JSON-LD algorithm, where the algorithm's use of dictionaries are replaced with JSON objects.
Note that the output above could easily be compacted using the technique outlined in the previous section. It is also possible to deserialize the JSON-LD document back to RDF using the Deserialize JSON-LD to RDF algorithm.
3. Conformance §
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MAY, MUST, and MUST NOT are to be interpreted as described in [RFC2119].
There are two classes of products that can claim conformance to this specification: JSON-LD Processors, and RDF Serializers/Deserializers.
A conforming JSON-LD Processor is a system which can perform the Expansion, Compaction, and Flattening operations in a manner consistent with the algorithms defined in this specification.
JSON-LD Processors MUST NOT attempt to correct malformed IRIs or language tags; however, they MAY issue validation warnings. IRIs are not modified other than conversion between relative and absolute IRIs.
A conforming RDF Serializer/Deserializer is a system that can deserialize JSON-LD to RDF and serialize RDF as JSON-LD as defined in this specification.
The algorithms in this specification are generally written with more concern for clarity than efficiency. Thus, JSON-LD Processors may implement the algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.
In algorithm steps that describe operations on keywords, those steps also apply to keyword aliases.
Implementers can partially check their level of conformance to this specification by successfully passing the test cases of the JSON-LD test suite [JSON-LD-TESTS]. Note, however, that passing all the tests in the test suite does not imply complete conformance to this specification. It only implies that the implementation conforms to aspects tested by the test suite.
4. Context Processing Algorithms §
4.1 Context Processing Algorithm §
When processing a JSON-LD data structure, each processing rule is applied using information provided by the active context. This section describes how to produce an active context.
The active context contains the active term definitions which specify how properties and values have to be interpreted as well as the current base IRI, the vocabulary mapping and the default language. Each term definition consists of an IRI mapping, a boolean flag reverse property, an optional type mapping or language mapping, an optional context, an optional nest value, an optional prefix flag, and an optional container mapping. A term definition can not only be used to map a term to an IRI, but also to map a term to a keyword, in which case it is referred to as a keyword alias.
When processing, active context is initialized without any term definitions, vocabulary mapping, or default language. If a local context is encountered during processing, a new active context is created by cloning the existing active context. Then the information from the local context is merged into the new active context. Given that local contexts may contain references to remote contexts, this includes their retrieval.
4.1.1 Overview §
This section is non-normative.
First we prepare a new active context result by cloning the current active context. Then we normalize the form of the original local context to an array. Local contexts may be in the form of a dictionary, a string, or an array containing a combination of the two. Finally we process each context contained in the local context array as follows.
Unless specified using
processingMode
API option,
the processing mode is set using the @version
member
in a local context and
affects the behavior of algorithms including expansion and compaction.
If context is a string, it represents a reference to
a remote context. We dereference the remote context and replace context
with the value of the @context
key of the top-level object in the
retrieved JSON-LD document. If there's no such key, an
invalid remote context
has been detected. Otherwise, we process context by recursively using
this algorithm ensuring that there is no cyclical reference.
If context is a dictionary, we first update the
base IRI, the vocabulary mapping, processing mode, and the
default language by processing three specific keywords:
@base
, @vocab
, @version
, and @language
.
These are handled before any other keys in the local context because
they affect how the other keys are processed. Please note that @base
is
ignored when processing remote contexts.
Then, for every other key in local context, we update the term definition in result. Since term definitions in a local context may themselves contain terms or compact IRIs, we may need to recurse. When doing so, we must ensure that there is no cyclical dependency, which is an error. After we have processed any term definition dependencies, we update the current term definition, which may be a keyword alias.
Finally, we return result as the new active context.
4.1.2 Algorithm §
This algorithm specifies how a new active context is updated with a local context. The algorithm takes three input variables: an active context, a local context, and an array remote contexts which is used to detect cyclical context inclusions. If remote contexts is not passed, it is initialized to an empty array.
- Initialize result to the result of cloning active context.
- If local context is not an array, set it to an array containing only local context.
-
For each item context in local context:
- If context is
null
, set result to a newly-initialized active context and continue with the next context. In JSON-LD 1.0, the base IRI was given a default value here; this is now described conditionally in section 9. The Application Programming Interface. - If context is a string,
- Set context to the result of resolving value against the base IRI which is established as specified in section 5.1 Establishing a Base URI of [RFC3986]. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
- If context is in the remote contexts array, a
recursive context inclusion
error has been detected and processing is aborted; otherwise, add context to remote contexts. - If context was previously dereferenced, then the processor MUST NOT do a further dereference, and context is set to the previously established internal representation.
- Otherwise, dereference context, transforming into the internal representation.
If context cannot be dereferenced,
or cannot be transformed into the internal representation,
a
loading remote context failed
error has been detected and processing is aborted. If the dereferenced document has no top-level dictionary with an@context
member, aninvalid remote context
has been detected and processing is aborted; otherwise, set context to the value of that member. - Set result to the result of recursively calling this algorithm, passing result for active context, context for local context, and a copy of remote contexts.
- Continue with the next context.
- If context is not a dictionary, an
invalid local context
error has been detected and processing is aborted. - If context has an
@base
key and remote contexts is empty, i.e., the currently being processed context is not a remote context:- Initialize value to the value associated with the
@base
key. - If value is
null
, remove the base IRI of result. - Otherwise, if value is an absolute IRI, the base IRI of result is set to value.
- Otherwise, if value is a relative IRI and
the base IRI of result is not
null
, set the base IRI of result to the result of resolving value against the current base IRI of result. - Otherwise, an
invalid base IRI
error has been detected and processing is aborted.
- Initialize value to the value associated with the
- If context has an
@version
key:- If the associated value is not
1.1
, aninvalid @version value
has been detected, and processing is aborted. - If processing mode
is set to
json-ld-1.0
, aprocessing mode conflict
error has been detected and processing is aborted. - Set processing mode,
to
json-ld-1.1
, if not already set.
- If the associated value is not
- If context has an
@vocab
key:- Initialize value to the value associated with the
@vocab
key. - If value is null, remove any vocabulary mapping from result.
- Otherewise, if value
the empty string (
""
), the effective value is the current base IRI. - Otherwise, if value is
an absolute IRI
or blank node identifier, the vocabulary mapping
of result is set to value. If it is not
an absolute IRI, or a blank node identifier, an
invalid vocab mapping
error has been detected and processing is aborted.
- Initialize value to the value associated with the
- If context has an
@language
key:- Initialize value to the value associated with the
@language
key. - If value is
null
, remove any default language from result. - Otherwise, if value is string, the
default language of result is set to
lowercased value. If it is not a string, an
invalid default language
error has been detected and processing is aborted.
- Initialize value to the value associated with the
- Create a dictionary defined to use to keep track of whether or not a term has already been defined or currently being defined during recursion.
- For each key-value pair in context where
key is not
@base
,@vocab
, or@language
, invoke the Create Term Definition algorithm, passing result for active context, context for local context, key, and defined.
- If context is
- Return result.
4.2 Create Term Definition §
This algorithm is called from the Context Processing algorithm to create a term definition in the active context for a term being processed in a local context.
4.2.1 Overview §
This section is non-normative.
term definitions are created by parsing the information in the given local context for the given term. If the given term is a compact IRI, it may omit an IRI mapping by depending on its prefix having its own term definition. If the prefix is a key in the local context, then its term definition must first be created, through recursion, before continuing. Because a term definition can depend on other term definitions, a mechanism must be used to detect cyclical dependencies. The solution employed here uses a map, defined, that keeps track of whether or not a term has been defined or is currently in the process of being defined. This map is checked before any recursion is attempted.
After all dependencies for a term have been defined, the rest of the information in the local context for the given term is taken into account, creating the appropriate IRI mapping, container mapping, and type mapping or language mapping for the term.
4.2.2 Algorithm §
The algorithm has four required inputs which are: an active context, a local context, a term, and a map defined.
- If defined contains the key term and the associated
value is
true
(indicating that the term definition has already been created), return. Otherwise, if the value isfalse
, acyclic IRI mapping
error has been detected and processing is aborted. - Set the value associated with defined's term key to
false
. This indicates that the term definition is now being created but is not yet complete. - Since keywords cannot be overridden,
term must not be a keyword. Otherwise, a
keyword redefinition
error has been detected and processing is aborted. - Remove any existing term definition for term in active context.
- Initialize value to a copy of the value associated with the key term in local context.
- If value is
null
or value is a dictionary containing the key-value pair@id
-null
, set the term definition in active context tonull
, set the value associated with defined's key term totrue
, and return. - Otherwise, if value is a string, convert it
to a dictionary consisting of a single member whose
key is
@id
and whose value is value. Set simple term totrue
. - Otherwise, value must be a dictionary, if not, an
invalid term definition
error has been detected and processing is aborted. Set simple term tofalse
. - Create a new term definition, definition.
- If value contains the key
@type
:- Initialize type to the value associated with the
@type
key, which must be a string. Otherwise, aninvalid type mapping
error has been detected and processing is aborted. - Set type to the result of using the
IRI Expansion algorithm, passing
active context, type for value,
true
for vocab, local context, and defined. If the expanded type is neither@id
, nor@vocab
, nor an absolute IRI, aninvalid type mapping
error has been detected and processing is aborted. - Set the type mapping for definition to type.
- Initialize type to the value associated with the
- If value contains the key
@reverse
:- If value contains
@id
or@nest
, members, aninvalid reverse property
error has been detected and processing is aborted. - If the value associated with the
@reverse
key is not a string, aninvalid IRI mapping
error has been detected and processing is aborted. - Otherwise, set the IRI mapping of definition to the
result of using the IRI Expansion algorithm,
passing active context, the value associated with
the
@reverse
key for value,true
for vocab, local context, and defined. If the result is neither an absolute IRI nor a blank node identifier, i.e., it contains no colon (:
), aninvalid IRI mapping
error has been detected and processing is aborted. - If value contains an
@container
member, set the container mapping of definition to its value; if its value is neither@set
, nor@index
, nornull
, aninvalid reverse property
error has been detected (reverse properties only support set- and index-containers) and processing is aborted. - Set the reverse property flag of definition
to
true
. - Set the term definition of term in
active context to definition and the
value associated with defined's key term to
true
and return.
- If value contains
- Set the reverse property flag of definition
to
false
. - If value contains the key
@id
and its value does not equal term:- If the value associated with the
@id
key is not a string, aninvalid IRI mapping
error has been detected and processing is aborted. - Otherwise, set the IRI mapping of definition to the
result of using the IRI Expansion algorithm, passing
active context, the value associated with the
@id
key for value,true
for vocab, local context, and defined. If the resulting IRI mapping is neither a keyword, nor an absolute IRI, nor a blank node identifier, aninvalid IRI mapping
error has been detected and processing is aborted; if it equals@context
, aninvalid keyword alias
error has been detected and processing is aborted. - If term does not contain a colon (
:
), simple term istrue
, and the, IRI mapping of definition ends with a URI gen-delim character, set the prefix flag in definition totrue
.
- If the value associated with the
-
Otherwise if the term contains a colon (
:
):- If term is a compact IRI with a prefix that is a key in local context a dependency has been found. Use this algorithm recursively passing active context, local context, the prefix as term, and defined.
- If term's prefix has a term definition in active context, set the IRI mapping of definition to the result of concatenating the value associated with the prefix's IRI mapping and the term's suffix.
- Otherwise, term is an absolute IRI or blank node identifier. Set the IRI mapping of definition to term.
- Otherwise, if active context has a
vocabulary mapping, the IRI mapping
of definition is set to the result of concatenating the value
associated with the vocabulary mapping and term.
If it does not have a vocabulary mapping, an
invalid IRI mapping
error been detected and processing is aborted. - If value contains the key
@container
:- Initialize container to the value associated with the
@container
key, which must be either@graph
,@id
,@index
,@language
,@list
,@set
, or@type
. or an array containing exactly any one of those keywords, an array containing@graph
and either@id
or@index
optionally including@set
, or an array containing a combination of@set
and any of@index
,@id
,@type
,@language
in any order . Otherwise, aninvalid container mapping
has been detected and processing is aborted. - If
processingMode
isjson-ld-1.0
and the container value is@graph
,@id
, or@type
, or is otherwise not a string, aninvalid container mapping
has been detected and processing is aborted. - Set the container mapping of definition to container.
- Initialize container to the value associated with the
- If value contains the key
@context
:- If
processingMode
isjson-ld-1.0
, aninvalid term definition
has been detected and processing is aborted. - Initialize context to the value associated with the
@context
key, which is treated as a local context. - Invoke the Context Processing algorithm
using the active context and context as local context.
If any error is detected, an
invalid scoped context
error has been detected and processing is aborted. - Set the local context of definition to context.
- If
- If value contains the key
@language
and does not contain the key@type
:- Initialize language to the value associated with the
@language
key, which must be eithernull
or a string. Otherwise, aninvalid language mapping
error has been detected and processing is aborted. - If language is a string set it to lowercased language. Set the language mapping of definition to language.
- Initialize language to the value associated with the
- If value contains the key
@nest
:- If
processingMode
isjson-ld-1.0
, aninvalid term definition
has been detected and processing is aborted. - Initialize nest value in defined to the value associated with the
@nest
key, which must be a string and must not be a keyword other than@nest
. Otherwise, aninvalid @nest value
error has been detected and processing is aborted.
- If
- If value contains the key
@prefix
:- If
processingMode
isjson-ld-1.0
, or if term contains a colon (:
), aninvalid term definition
has been detected and processing is aborted. - Initialize the prefix flag to the value associated with the
@prefix
key, which must be a boolean. Otherwise, aninvalid @prefix value
error has been detected and processing is aborted.
- If
- If the value contains any key other than
@id
,@reverse
,@container
,@context
,@nest
,@prefix
, or@type
, aninvalid term definition
error has been detected and processing is aborted. - Set the term definition of term in
active context to definition and set the value
associated with defined's key term to
true
.
4.3 IRI Expansion §
In JSON-LD documents, some keys and values may represent IRIs. This section defines an algorithm for transforming a string that represents an IRI into an absolute IRI or blank node identifier. It also covers transforming keyword aliases into keywords.
IRI expansion may occur during context processing or during any of the other JSON-LD algorithms. If IRI expansion occurs during context processing, then the local context and its related defined map from the Context Processing algorithm are passed to this algorithm. This allows for term definition dependencies to be processed via the Create Term Definition algorithm.
4.3.1 Overview §
This section is non-normative.
In order to expand value to an absolute IRI, we must
first determine if it is null
, a term, a
keyword alias, or some form of IRI. Based on what
we find, we handle the specific kind of expansion; for example, we expand
a keyword alias to a keyword and a term
to an absolute IRI according to its IRI mapping
in the active context. While inspecting value we
may also find that we need to create term definition
dependencies because we're running this algorithm during context processing.
We can tell whether or not we're running during context processing by
checking local context against null
.
We know we need to create a term definition in the
active context when value is
a key in the local context and the defined map
does not have a key for value with an associated value of
true
. The defined map is used during
Context Processing to keep track of
which terms have already been defined or are
in the process of being defined. We create a
term definition by using the
Create Term Definition algorithm.
4.3.2 Algorithm §
The algorithm takes two required and four optional input variables. The
required inputs are an active context and a value
to be expanded. The optional inputs are two flags,
document relative and vocab, that specifying
whether value can be interpreted as a relative IRI
against the document's base IRI or the
active context's
vocabulary mapping, respectively, and
a local context and a map defined to be used when
this algorithm is used during Context Processing.
If not passed, the two flags are set to false
and
local context and defined are initialized to null
.
- If value is a keyword or
null
, return value as is. - If local context is not
null
, it contains a key that equals value, and the value associated with the key that equals value in defined is nottrue
, invoke the Create Term Definition algorithm, passing active context, local context, value as term, and defined. This will ensure that a term definition is created for value in active context during Context Processing. - If active context has a term definition for value, and the associated IRI mapping is a keyword, return that keyword.
- If vocab is
true
and the active context has a term definition for value, return the associated IRI mapping. - If value contains a colon (
:
), it is either an absolute IRI, a compact IRI, or a blank node identifier:- Split value into a prefix and suffix
at the first occurrence of a colon (
:
). - If prefix is underscore (
_
) or suffix begins with double-forward-slash (//
), return value as it is already an absolute IRI or a blank node identifier. - If local context is not
null
, it contains a key that equals prefix, and the value associated with the key that equals prefix in defined is nottrue
, invoke the Create Term Definition algorithm, passing active context, local context, prefix as term, and defined. This will ensure that a term definition is created for prefix in active context during Context Processing. - If active context contains a term definition for prefix, return the result of concatenating the IRI mapping associated with prefix and suffix.
- Return value as it is already an absolute IRI.
- Split value into a prefix and suffix
at the first occurrence of a colon (
- If vocab is
true
, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value. - Otherwise, if document relative is
true
set value to the result of resolving value against the base IRI. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987]. - Return value as is.
5. Expansion Algorithms §
5.1 Expansion Algorithm §
This algorithm expands a JSON-LD document, such that all context definitions are removed, all terms and compact IRIs are expanded to absolute IRIs, blank node identifiers, or keywords and all JSON-LD values are expressed in arrays in expanded form.
5.1.1 Overview §
This section is non-normative.
Starting with its root element, we can process the JSON-LD document recursively, until we have a fully expanded result. When expanding an element, we can treat each one differently according to its type, in order to break down the problem:
- If the element is
null
, there is nothing to expand. - Otherwise, if element is a scalar, we expand it according to the Value Expansion algorithm.
- Otherwise, if the element is an array, then we expand each of its items recursively and return them in a new array.
- Otherwise, element is a dictionary. We expand each of its keys, adding them to our result, and then we expand each value for each key recursively. Some of the keys will be terms or compact IRIs and others will be keywords or simply ignored because they do not have definitions in the context. Any IRIs will be expanded using the IRI Expansion algorithm.
Finally, after ensuring result is in an array, we return result.
5.1.2 Algorithm §
The algorithm takes three required and one optional input variables.
The required inputs are an active context,
an active property, and an element to be expanded.
The optional input is the flag frame expansion the allows
special forms of input used for frame expansion.
To begin, the active property is set to null
,
and element is set to the JSON-LD input.
If not passed, the frame expansion flag is set to false
.
The algorithm also performs processing steps specific to expanding
a JSON-LD Frame. For a frame, the @id
and
@type
properties can accept an array of IRIs or
an empty dictionary. The properties of a value object can also
accept an array of strings, or an empty dictionary.
Framing also uses additional keyword properties:
(@explicit
, @default
,
@embed
, @explicit
, @omitDefault
, or
@requireAll
) which are preserved through expansion.
Special processing for a JSON-LD Frame is invoked when the
frame expansion flag is set to true
.
- If element is
null
, returnnull
. - If active property is
@default
, set the frame expansion flag tofalse
. - If element is a scalar,
- If active property is
null
or@graph
, drop the free-floating scalar by returningnull
. - Return the result of the Value Expansion algorithm, passing the active context, active property, and element as value.
- If active property is
- If element is an array,
- Initialize an empty array, result.
- For each item in element:
- Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, item as element, and the frame expansion flag.
- If the active property is
@list
or its container mapping includes@list
, the expanded item must not be an array or a list object, otherwise alist of lists
error has been detected and processing is aborted. - If expanded item is an array, append each of its items to result. Otherwise, if expanded item is not null, append it to result.
- Return result.
- Otherwise element is a dictionary.
- If element contains the key
@context
, set active context to the result of the Context Processing algorithm, passing active context and the value of the@context
key as local context. - For each key/value pair in element
where key expands to
@type
using the IRI Expansion algorithm, passing active context, key for value, andtrue
for vocab:- For each term which is a value of value ordered lexicographically, if term's term definition in active context has a local context, set active context to the result to the result of the Context Processing algorithm, passing active context and the value of the term's local context as local context.
- Initialize an empty dictionary, result.
- For each key and value in element,
ordered lexicographically by key:
- If key is
@context
, continue to the next key. - Set expanded property to the result of
using the IRI Expansion algorithm,
passing active context, key for
value, and
true
for vocab. - If expanded property is
null
or it neither contains a colon (:
) nor it is a keyword, drop key by continuing to the next key. - If expanded property is a keyword:
- If active property equals
@reverse
, aninvalid reverse property map
error has been detected and processing is aborted. - If result has already an expanded property member, a
colliding keywords
error has been detected and processing is aborted. - If expanded property is
@id
and value is not a string, aninvalid @id value
error has been detected and processing is aborted. Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context, value, andtrue
for document relative. When the frame expansion flag is set, value may be an empty dictionary, or an array of one or more strings. expanded value will be an array of one or more of these, with string values expanded using the IRI Expansion Algorithm. - If expanded property is
@type
and value is neither a string nor an array of strings, aninvalid type value
error has been detected and processing is aborted. Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context,true
for vocab, andtrue
for document relative to expand the value or each of its items. When the frame expansion flag is set, value may also be an empty dictionary. - If expanded property is
@graph
, set expanded value to the result of using this algorithm recursively passing active context,@graph
for active property, value for element, and the frame expansion flag, ensuring that expanded value is an array of one or more dictionaries. - If expanded property is
@value
and value is not a scalar ornull
, aninvalid value object value
error has been detected and processing is aborted. Otherwise, set expanded value to value. If expanded value isnull
, set the@value
member of result tonull
and continue with the next key from element. Null values need to be preserved in this case as the meaning of an@type
member depends on the existence of an@value
member. When the frame expansion flag is set, value may also be an empty dictionary or an array of scalar values. expanded value will be null, or an array of one or more scalar values. - If expanded property is
@language
and value is not a string, aninvalid language-tagged string
error has been detected and processing is aborted. Otherwise, set expanded value to lowercased value. When the frame expansion flag is set, value may also be an empty dictionary or an array of zero or strings. expanded value will be an array of one or more string values converted to lower case. - If expanded property is
@index
and value is not a string, aninvalid @index value
error has been detected and processing is aborted. Otherwise, set expanded value to value. - If expanded property is
@list
:- If active property is
null
or@graph
, continue with the next key from element to remove the free-floating list. - Otherwise, initialize expanded value to the result of using this algorithm recursively passing active context, active property, value for element, and the frame expansion flag.
- If expanded value is a list object, a
list of lists
error has been detected and processing is aborted.
- If active property is
- If expanded property is
@set
, set expanded value to the result of using this algorithm recursively, passing active context, active property, value for element, and the frame expansion flag. - If expanded property is
@reverse
and value is not a dictionary, aninvalid @reverse value
error has been detected and processing is aborted. Otherwise- Initialize expanded value to the result of using this
algorithm recursively, passing active context,
@reverse
as active property, value as element, and the frame expansion flag. - If expanded value contains an
@reverse
member, i.e., properties that are reversed twice, execute for each of its property and item the following steps:- If result does not have a property member, create one and set its value to an empty array.
- Append item to the value of the property member of result.
- If expanded value contains members other than
@reverse
:- If result does not have an
@reverse
member, create one and set its value to an empty dictionary. - Reference the value of the
@reverse
member in result using the variable reverse map. - For each property and items in expanded value
other than
@reverse
:- For each item in items:
- If item is a value object or list object, an
invalid reverse property value
has been detected and processing is aborted. - If reverse map has no property member, create one and initialize its value to an empty array.
- Append item to the value of the property member in reverse map.
- If item is a value object or list object, an
- For each item in items:
- If result does not have an
- Continue with the next key from element.
- Initialize expanded value to the result of using this
algorithm recursively, passing active context,
- If expanded property is
@nest
, add key to nests, initializing it to an empty array, if necessary. Continue with the next key from element. - When the frame expansion flag is set,
if expanded property is any other
framing keyword (
@explicit
,@default
,@embed
,@explicit
,@omitDefault
, or@requireAll
), set expanded value to the result of performing the Expansion Algorithm recursively, passing active context, active property, value for element, and the frame expansion flag. - Unless expanded value is
null
, set the expanded property member of result to expanded value. - Continue with the next key from element.
- If active property equals
- If key's term definition in active context has a local context, set term context to the result of the Context Processing algorithm, passing active context and the value of the key's local context as local context. Otherwise, set term context to active context.
- Set container mapping to key's container mapping in term context.
- If container mapping includes
@language
and value is a dictionary then value is expanded from a language map as follows:- Initialize expanded value to an empty array.
- For each key-value pair language-language value
in value, ordered lexicographically by language:
- If language value is not an array set it to an array containing only language value.
- For each item in language value:
- item must be a string
or
null
, otherwise aninvalid language map value
error has been detected and processing is aborted. - Append a dictionary to
expanded value that consists of two
key-value pairs: (
@value
-item) and (@language
-lowercased language), unless item isnull
. If language is@none
, or expands to@none
, do not set the@language
member.
- item must be a string
or
- Otherwise, if container mapping
includes
@index
,@type
, or@id
and value is a dictionary then value is expanded from an map as follows:- Initialize expanded value to an empty array.
- For each key-value pair index-index value
in value, ordered lexicographically by index:
- If container mapping includes
@type
, and index's term definition in term context has a local context, set map context to the result of the Context Processing algorithm, passing term context as active context and the value of the index's local context as local context. Otherwise, set map context to term context. - Set expanded index to the result of using the
IRI Expansion algorithm,
passing active context, index, and
true
for vocab. - If index value is not an array set it to an array containing only index value.
- Initialize index value to the result of using this algorithm recursively, passing map context as active context, key as active property, index value as element, and the frame expansion flag.
- For each item in index value:
- If container mapping includes
@graph
and if item is not a graph object, set item to a new dictionary containing the key-value pair@graph
-item, ensuring that the value is represented using an array. - If container mapping includes
@index
and item does not have the key@index
and expanded index is not@none
, add the key-value pair (@index
-index) to item. - Otherwise, if container mapping includes
@id
and item does not have the key@id
, add the key-value pair (@id
-expanded index) to item, where expanded index is set to the result of using the IRI Expansion algorithm, passing active context, index, andtrue
for document relative, unless expanded index is already set to@none
. - Otherwise, if container mapping includes
@type
set types to the concatenation of expanded index with any existing values of@type
in item. If expanded index is@none
, do not concatenate expanded index to types. Add the key-value pair (@type
-types) to item. - Append item to expanded value.
- If container mapping includes
- If container mapping includes
- Otherwise, initialize expanded value to the result of using this algorithm recursively, passing term context as active context, key for active property, value for element, and the frame expansion flag.
- If expanded value is
null
, ignore key by continuing to the next key from element. - If container mapping includes
@list
and expanded value is not already a list object, convert expanded value to a list object by first setting it to an array containing only expanded value if it is not already an array, and then by setting it to a dictionary containing the key-value pair@list
-expanded value. - If container mapping includes
@graph
, convert expanded value into an array, if necessary, then convert each value ev in expanded value into a graph object:- If ev is not a graph object, convert it into
one by creating a dictionary containing the key-value
pair
@graph
-ev where ev is represented as an array.
- If ev is not a graph object, convert it into
one by creating a dictionary containing the key-value
pair
- Otherwise, if the term definition associated to
key indicates that it is a reverse property
- If result has no
@reverse
member, create one and initialize its value to an empty dictionary. - Reference the value of the
@reverse
member in result using the variable reverse map. - If expanded value is not an array, set it to an array containing expanded value.
- For each item in expanded value
- If item is a value object or list object, an
invalid reverse property value
has been detected and processing is aborted. - If reverse map has no expanded property member, create one and initialize its value to an empty array.
- Append item to the value of the expanded property member of reverse map.
- If item is a value object or list object, an
- If result has no
- Otherwise, if key is not a reverse property:
- If result does not have an expanded property member, create one and initialize its value to an empty array.
- Append expanded value to value of the expanded property member of result.
- For each key nesting-key in nests
- Set nested values to the value of nesting-key in element, ensuring that it is an array.
- For each nested value in nested values:
- If nested value is not a dictionary, or any key within
nested value expands to
@value
, aninvalid @nest value
error has been detected and processing is aborted. - Recursively repeat step 7 using nested value for element.
- If nested value is not a dictionary, or any key within
nested value expands to
- If key is
- If result contains the key
@value
:- The result must not contain any keys other than
@value
,@language
,@type
, and@index
. It must not contain both the@language
key and the@type
key. Otherwise, aninvalid value object
error has been detected and processing is aborted. - If the value of result's
@value
key isnull
, then set result tonull
. - Otherwise, if the value of result's
@value
member is not a string and result contains the key@language
, aninvalid language-tagged value
error has been detected (only strings can be language-tagged) and processing is aborted. - Otherwise, if the result has an
@type
member and its value is not an IRI, aninvalid typed value
error has been detected and processing is aborted.
- The result must not contain any keys other than
- Otherwise, if result contains the key
@type
and its associated value is not an array, set it to an array containing only the associated value. - Otherwise, if result contains the key
@set
or@list
:- The result must contain at most one other key and that
key must be
@index
. Otherwise, aninvalid set or list object
error has been detected and processing is aborted. - If result contains the key
@set
, then set result to the key's associated value.
- The result must contain at most one other key and that
key must be
- If result contains only the key
@language
, set result tonull
. - If active property is
null
or@graph
, drop free-floating values as follows:- If result is an empty dictionary or contains
the keys
@value
or@list
, set result tonull
. - Otherwise, if result is a dictionary whose only
key is
@id
, set result tonull
. When the frame expansion flag is set, a dictionary containing only the@id
key is retained.
- If result is an empty dictionary or contains
the keys
- Return result.
If, after the above algorithm is run, the result is a
dictionary that contains only an @graph
key, set the
result to the value of @graph
's value. Otherwise, if the result
is null
, set it to an empty array. Finally, if
the result is not an array, then set the result to an
array containing only the result.
5.2 Value Expansion §
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. A value is said to be in expanded form after the application of this algorithm.
5.2.1 Overview §
This section is non-normative.
If active property has a type mapping in the
active context set to @id
or @vocab
,
and the value is a string,
a dictionary with a single member @id
whose
value is the result of using the
IRI Expansion algorithm on value
is returned.
Otherwise, the result will be a dictionary containing
an @value
member whose value is the passed value.
Additionally, an @type
member will be included if there is a
type mapping associated with the active property
or an @language
member if value is a
string and there is language mapping associated
with the active property.
Note that values interpreted as IRIs fall into two categories:
those that are document relative, and those that are
vocabulary relative. Properties and values of @type
,
along with terms marked as "@type": "@vocab"
are vocabulary relative, meaning that they need to be either
a defined term, a compact IRI
where the prefix is a term,
or a string which is turned into an absolute IRI using
the vocabulary mapping.
5.2.2 Algorithm §
The algorithm takes three required inputs: an active context, an active property, and a value to expand.
- If the active property has a type mapping
in active context that is
@id
, and the value is a string, return a new dictionary containing a single key-value pair where the key is@id
and the value is the result of using the IRI Expansion algorithm, passing active context, value, andtrue
for document relative. - If active property has a type mapping in
active context that is
@vocab
, and the value is a string, return a new dictionary containing a single key-value pair where the key is@id
and the value is the result of using the IRI Expansion algorithm, passing active context, value,true
for vocab, andtrue
for document relative. - Otherwise, initialize result to a dictionary
with an
@value
member whose value is set to value. - If active property has a type mapping in
active context,
other than
@id
or@vocab
, add an@type
member to result and set its value to the value associated with the type mapping. - Otherwise, if value is a string:
- If a language mapping is associated with
active property in active context,
add an
@language
to result and set its value to the language code associated with the language mapping; unless the language mapping is set tonull
in which case no member is added. - Otherwise, if the active context has a
default language, add an
@language
to result and set its value to the default language.
- If a language mapping is associated with
active property in active context,
add an
- Return result.
6. Compaction Algorithms §
6.1 Compaction Algorithm §
This algorithm compacts a JSON-LD document, such that the given context is applied. This must result in shortening any applicable IRIs to terms or compact IRIs, any applicable keywords to keyword aliases, and any applicable JSON-LD values expressed in expanded form to simple values such as strings or numbers.
6.1.1 Overview §
This section is non-normative.
Starting with its root element, we can process the JSON-LD document recursively, until we have a fully compacted result. When compacting an element, we can treat each one differently according to its type, in order to break down the problem:
- If the element is a scalar, it is already in compacted form, so we simply return it.
- If the element is an array, we compact each of its items recursively and return them in a new array.
- Otherwise element is a dictionary. The value
of each key in element is compacted recursively. Some of the keys will be
compacted, using the IRI Compaction algorithm,
to terms or compact IRIs
and others will be compacted from keywords to
keyword aliases or simply left
unchanged because they do not have definitions in the context.
Values will be converted to compacted form via the
Value Compaction algorithm. Some data
will be reshaped based on container mapping
specified in the context such as
@index
or@language
maps.
The final output is a dictionary with an @context
key, if a non-empty context was given, where the dictionary
is either result or a wrapper for it where result appears
as the value of an (aliased) @graph
key because result
contained two or more items in an array.
6.1.2 Algorithm §
The algorithm takes five required input variables: an active context,
an inverse context, an active property, an
element to be compacted, and a flag
compactArrays
To begin, the active context is set to the result of
performing Context Processing
on the passed context, the inverse context is
set to the result of performing the
Inverse Context Creation algorithm
on active context, the active property is
set to null
, element is set to the result of
performing the Expansion algorithm
on the JSON-LD input, and, if not passed,
compactArrays
is set to true
.
- If the term definition for active property has a
local context:
- Set active context to the result of the Context Processing algorithm, passing active context and the value of the active property's local context as local context.
- Set inverse context using the Inverse Context Creation algorithm using active context.
- If element is a scalar, it is already in its most compact form, so simply return element.
- If element is an array:
- Initialize result to an empty array.
- For each item in element:
- Initialize compacted item to the result of using this algorithm recursively, passing active context, inverse context, active property, and item for element.
- If compacted item is not
null
, then append it to result.
- If result contains only one item (it has a length of
1
), active property is not@graph
or@set
, or the container mapping for active property in active context does not include@list
or@set
, andcompactArrays
istrue
, set result to its only item. - Return result.
- Otherwise element is a dictionary.
If element has an
@value
or@id
member and the result of using the Value Compaction algorithm, passing active context, inverse context, active property,and element as value is a scalar, return that result. - Initialize inside reverse to
true
if active property equals@reverse
, otherwise tofalse
. - Initialize result to an empty dictionary.
- If element has a
@type
member, create a new array compacted types initialized by transforming each expanded type of that member into it's compacted form using the IRI Compaction algorithm, passing active context, inverse context, expanded type for var, andtrue
for vocab. Then, for each term in compacted types ordered lexicographically:- If the term definition for term has a
local context:
- Set active context to the result of the Context Processing algorithm, passing active context and the value of term's local context as local context.
- Set inverse context using the Inverse Context Creation algorithm using active context.
- If the term definition for term has a
local context:
- For each key expanded property and value expanded value
in element, ordered lexicographically by expanded property:
- If expanded property is
@id
or@type
:- If expanded value is a string,
then initialize compacted value to the result
of using the IRI Compaction algorithm,
passing active context, inverse context,
expanded value for var,
and
true
for vocab if expanded property is@type
,false
otherwise. - Otherwise, expanded value must be a
@type
array:- Initialize compacted value to an empty array.
- For each item expanded type in
expanded value:
- Set term to the result of
of using the IRI Compaction algorithm,
passing active context, inverse context,
expanded type for var, and
true
for vocab. - Append term, to compacted value.
- Set term to the result of
of using the IRI Compaction algorithm,
passing active context, inverse context,
expanded type for var, and
- If compacted value contains only one
item (it has a length of
1
), then set compacted value to its only item.
- Initialize alias to the result of using the
IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
and
true
for vocab. - Add a member alias to result whose value is set to compacted value and continue to the next expanded property.
- If expanded value is a string,
then initialize compacted value to the result
of using the IRI Compaction algorithm,
passing active context, inverse context,
expanded value for var,
and
- If expanded property is
@reverse
:- Initialize compacted value to the result of using this
algorithm recursively, passing active context,
inverse context,
@reverse
for active property, and expanded value for element. - For each property and value in compacted value:
- If the term definition for property in the
active context indicates that property is
a reverse property
- If the term definition for property in
the active context has a
container mapping including
@set
orcompactArrays
isfalse
, and value is not an array, set value to a new array containing only value. - If property is not a member of result, add one and set its value to value.
- Otherwise, if the value of the property member of result is not an array, set it to a new array containing only the value. Then append value to its value if value is not an array, otherwise append each of its items.
- Remove the property member from compacted value.
- If the term definition for property in
the active context has a
container mapping including
- If the term definition for property in the
active context indicates that property is
a reverse property
- If compacted value has some remaining members, i.e.,
it is not an empty dictionary:
- Initialize alias to the result of using the
IRI Compaction algorithm,
passing active context, inverse context,
@reverse
for var, andtrue
for vocab. - Set the value of the alias member of result to compacted value.
- Initialize alias to the result of using the
IRI Compaction algorithm,
passing active context, inverse context,
- Continue with the next expanded property from element.
- Initialize compacted value to the result of using this
algorithm recursively, passing active context,
inverse context,
- If expanded property is
@preserve
then:- Initialize compacted value to the result of using this algorithm recursively, passing active context, inverse context, property for active property, and expanded value for element.
- Add expanded value as the value of
@preserve
in result unless expanded value is an empty array.
- If expanded property is
@index
and active property has a container mapping in active context that includes@index
, then the compacted result will be inside of an@index
container, drop the@index
property by continuing to the next expanded property. - Otherwise, if expanded property is
@index
,@value
, or@language
:- Initialize alias to the result of using
the IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
and
true
for vocab. - Add a member alias to result whose value is set to expanded value and continue with the next expanded property.
- Initialize alias to the result of using
the IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
and
- If expanded value is an empty array:
- Initialize item active property to the result of
using the IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
expanded value for value,
true
for vocab, and inside reverse. - If the term definition for item active property
in the active context has a nest value, that value (nest term) must be
@nest
, or a term in the active context that expands to@nest
, otherwise an invalid @nest value error has been detected, and processing is aborted. If result does not have the key that equals nest term, initialize it to an empty JSON object (nest object). If nest object does not have the key that equals item active property, set this key's value in nest object to an empty array.Otherwise, if the key's value is not an array, then set it to one containing only the value. - Otherwise, if result does not have the key that equals item active property, set this key's value in result to an empty array. Otherwise, if the key's value is not an array, then set it to one containing only the value.
- Initialize item active property to the result of
using the IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
expanded value for value,
-
At this point, expanded value must be an
array due to the
Expansion algorithm.
For each item expanded item in expanded value:
- Initialize item active property to the result of using
the IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
expanded item for value,
true
for vocab, and inside reverse. - If the term definition for item active property
in the active context has a nest value
member, that value (nest term) must be
@nest
, or a term in the active context that expands to@nest
, otherwise an invalid @nest value error has been detected, and processing is aborted. Set nest result to the value of nest term in result, initializing it to a new dictionary, if necessary; otherwise set nest result to result. - Initialize container to
null
. If there is a container mapping for item active property in active context, set container to the first such value other than@set
. - Initialize as array to
true
orfalse
depending on if the container mapping for item active property in active context includes@set
or if item active property is@graph
or@list
. - Initialize compacted item to the result of using
this algorithm recursively, passing
active context, inverse context,
item active property for active property,
expanded item for element if it does
not contain the key
@list
and is not a graph object containing@list
, otherwise pass the key's associated value for element. -
If expanded item is a list object:
- If compacted item is not an array, then set it to an array containing only compacted item.
- If container is not
@list
:- Convert compacted item to a
list object by setting it to a
dictionary containing key-value pair
where the key is the result of the
IRI Compaction algorithm,
passing active context, inverse context,
@list
for var, and compacted item for value and the value is the original compacted item. - If expanded item contains the key
@index
, then add a key-value pair to compacted item where the key is the result of the IRI Compaction algorithm, passing active context, inverse context,@index
as var, and the value associated with the@index
key in expanded item as value.
- Convert compacted item to a
list object by setting it to a
dictionary containing key-value pair
where the key is the result of the
IRI Compaction algorithm,
passing active context, inverse context,
- Otherwise, item active property must not be a key
in nest result because there cannot be two
list objects associated
with an active property that has a
container mapping; a
compaction to list of lists
error has been detected and processing is aborted.
-
If expanded item is a graph object:
- If container includes
@graph
and@id
:- Initialize map object to the value of item active property in nest result.
- Initialize map key to the result of calling the
IRI Compaction algorithm
passing active context and the value of
@id
in expanded item or@none
if no such value exists as var, with vocab set totrue
if there is no@id
member in expanded item. - If compacted item is not an
array and as array is
true
, set compacted item to an array containing that value. - If map key is not a key in map object, then set this key's value in map object to compacted item. Otherwise, if the value is not an array, then set it to one containing only the value and then append compacted item to it.
- Otherwise, if container includes
@graph
and@index
and expanded item is a simple graph object:- Initialize map object to the value of item active property in nest result.
- Initialize map key the value of
@index
in expanded item or@none
, if no such value exists. - If compacted item is not an
array and as array is
true
, set compacted item to an array containing that value. - If map key is not a key in map object, then set this key's value in map object to compacted item. Otherwise, if the value is not an array, then set it to one containing only the value and then append compacted item to it.
- Otherwise, if container includes
@graph
and expanded item is a simple graph object the value cannot be represented as a map object. If compacted item is not an array and as array istrue
, set compacted item to an array containing that value. If the value associated with the key that equals item active property in nest result is not an array, set it to a new array containing only the value. Then append compacted item to the value if compacted item is not an array, otherwise, concatenate it. - Otherwise, container does not include
@graph
or otherwise does not match one of the previous cases, redo compacted item.- Set compacted item to a new dictionary containing
the key resulting from calling the IRI Compaction algorithm
passing active context,
@graph
as var, andtrue
for vocab using the original compacted item as a value. - If expanded item contains the key
@id
, add the key resulting from calling the IRI Compaction algorithm passing active context,@id
as var, andtrue
for vocab using the value resulting from calling the IRI Compaction algorithm passing active context, the value of@id
in expanded item as var. - If expanded item contains the key
@index
, add the key resulting from calling the IRI Compaction algorithm passing active context,@index
as var, andtrue
for vocab using the value of@index
in expanded item. - If as array is
true
, set compacted item to an array containing that value. - Then append compacted item to the value if compacted item is not an array, otherwise, concatenate it.
- Set compacted item to a new dictionary containing
the key resulting from calling the IRI Compaction algorithm
passing active context,
- If container includes
-
Otherwise, if container includes
@language
,@index
,@id
, or@type
and container does not include@graph
:- If item active property is not a key in nest result, initialize it to an empty dictionary. Initialize map object to the value of item active property in nest result.
- Set container key to the result of calling the
IRI Compaction algorithm
passing active context,
either
@language
,@index
,@id
, or@type
based on the contents of container, as var, andtrue
for vocab. - If container includes
@language
and expanded item contains the key@value
, then set compacted item to the value associated with its@value
key. Set map key to the value of@language
in expanded item, if any. - If container includes
@index
set map key to the value of@index
in expanded item, if any, and remove container key from compacted item. - If container includes
@id
, set map key to the value of container key in compacted item and remove container key from compacted item. - If container is
@type
, set map key to the first value of container key in compacted item, if any. If there are remaining values in compacted item for compacted container, set the value of compacted container in compacted value to those remaining values. Otherwise, remove that key-value pair from compacted item. - If compacted item is not an
array and as array is
true
, set compacted item to an array containing that value. - If map key is
null
, set it to the result of calling the IRI Compaction algorithm passing active context,@none
as var, andtrue
for vocab. - If map key is not a key in map object, then set this key's value in map object to compacted item. Otherwise, if the value is not an array, then set it to one containing only the value and then append compacted item to it.
-
Otherwise,
- If
compactArrays
isfalse
, as array istrue
and compacted item is not an array, set it to a new array containing only compacted item. - If item active property is not a key in result then add the key-value pair, (item active property-compacted item), to nest result.
- Otherwise, if the value associated with the key that equals item active property in nest result is not an array, set it to a new array containing only the value. Then append compacted item to the value if compacted item is not an array, otherwise, concatenate it.
- If
- Initialize item active property to the result of using
the IRI Compaction algorithm,
passing active context, inverse context,
expanded property for var,
expanded item for value,
- If expanded property is
- Return result.
If, after the algorithm outlined above is run, result
is an empty array, replace it with a new dictionary.
Otherwise, if result is an array, replace it with a new
dictionary with a single member whose key is the result
of using the IRI Compaction algorithm,
passing active context, inverse context, and
@graph
as var and whose value is the array
result.
Finally, if a non-empty context has been passed,
add an @context
member to result and set its value
to the passed context.
6.2 Inverse Context Creation §
When there is more than one term that could be chosen to compact an IRI, it has to be ensured that the term selection is both deterministic and represents the most context-appropriate choice whilst taking into consideration algorithmic complexity.
In order to make term selections, the concept of an inverse context is introduced. An inverse context is essentially a reverse lookup table that maps container mapping, type mappings, and language mappings to a simple term for a given active context. A inverse context only needs to be generated for an active context if it is being used for compaction.
To make use of an inverse context, a list of preferred container mapping and the type mapping or language mapping are gathered for a particular value associated with an IRI. These parameters are then fed to the Term Selection algorithm, which will find the term that most appropriately matches the value's mappings.
6.2.1 Overview §
This section is non-normative.
To create an inverse context for a given
active context, each term in the
active context is visited, ordered by length, shortest
first (ties are broken by choosing the lexicographically least
term). For each term, an entry is added to
the inverse context for each possible combination of
container mapping and type mapping
or language mapping that would legally match the
term. Illegal matches include differences between a
value's type mapping or language mapping and
that of the term. If a term has no
container mapping, type mapping, or
language mapping (or some combination of these), then it
will have an entry in the inverse context using the special
key @none
. This allows the
Term Selection algorithm to fall back
to choosing more generic terms when a more
specifically-matching term is not available for a particular
IRI and value combination.
6.2.2 Algorithm §
The algorithm takes one required input: the active context that the inverse context is being created for.
- Initialize result to an empty dictionary.
- Initialize default language to
@none
. If the active context has a default language, set default language to it. - For each key term and value term definition in
the active context, ordered by shortest term
first (breaking ties by choosing the lexicographically least
term):
- If the term definition is
null
, term cannot be selected during compaction, so continue to the next term. - Initialize container to
@none
. If the container mapping is not empty, set container to the concatenation of all values of the container mapping in lexicographically order . - Initialize var to the value of the IRI mapping for the term definition.
- If var is not a key in result, add a key-value pair where the key is var and the value is an empty dictionary to result.
- Reference the value associated with the var member in result using the variable container map.
- If container map has no container member,
create one and set its value to a new
dictionary with three members.
The first member is
@language
and its value is a new empty dictionary, the second member is@type
and its value is a new empty dictionary, and the third member is@any
and its value is a new dictionary with the member@none
set to the term being processed. - Reference the value associated with the container member in container map using the variable type/language map.
- If the term definition indicates that the term
represents a reverse property:
- Reference the value associated with the
@type
member in type/language map using the variable type map. - If type map does not have an
@reverse
member, create one and set its value to the term being processed.
- Reference the value associated with the
- Otherwise, if term definition has a
type mapping:
- Reference the value associated with the
@type
member in type/language map using the variable type map. - If type map does not have a member corresponding to the type mapping in term definition, create one and set its value to the term being processed.
- Reference the value associated with the
- Otherwise, if term definition has a
language mapping (might be
null
):- Reference the value associated with the
@language
member in type/language map using the variable language map. - If the language mapping equals
null
, set language to@null
; otherwise set it to the language code in language mapping. - If language map does not have a language member, create one and set its value to the term being processed.
- Reference the value associated with the
- Otherwise:
- Reference the value associated with the
@language
member in type/language map using the variable language map. - If language map does not have a default language member, create one and set its value to the term being processed.
- If language map does not have an
@none
member, create one and set its value to the term being processed. - Reference the value associated with the
@type
member in type/language map using the variable type map. - If type map does not have an
@none
member, create one and set its value to the term being processed.
- Reference the value associated with the
- If the term definition is
- Return result.
6.3 IRI Compaction §
This algorithm compacts an IRI to a term or compact IRI, or a keyword to a keyword alias. A value that is associated with the IRI may be passed in order to assist in selecting the most context-appropriate term.
6.3.1 Overview §
This section is non-normative.
If the passed IRI is null
, we simply
return null
. Otherwise, we first try to find a term
that the IRI or keyword can be compacted to if
it is relative to active context's
vocabulary mapping. In order to select the most appropriate
term, we may have to collect information about the passed
value. This information includes which
container mapping
would be preferred for expressing the value, and what its
type mapping or language mapping is. For
JSON-LD lists, the type mapping
or language mapping will be chosen based on the most
specific values that work for all items in the list. Once this
information is gathered, it is passed to the
Term Selection algorithm, which will
return the most appropriate term to use.
If no term was found that could be used to compact the
IRI, an attempt is made to compact the IRI using the
active context's vocabulary mapping,
if there is one. If the IRI could not be compacted, an
attempt is made to find a compact IRI.
A term will be used to create a compact IRI
only if the term definition contains the prefix flag
with the value true
.
If there is no appropriate compact IRI,
and the compactToRelative
option is true
,
the IRI is
transformed to a relative IRI using the document's
base IRI. Finally, if the IRI or
keyword still could not be compacted, it is returned
as is.
6.3.2 Algorithm §
This algorithm takes three required inputs and three optional inputs.
The required inputs are an active context, an inverse context,
and the var to be compacted. The optional inputs are a value associated
with the var, a vocab flag which specifies whether the
passed var should be compacted using the
active context's
vocabulary mapping, and a reverse flag which specifies whether
a reverse property is being compacted. If not passed, value is set to
null
and vocab and reverse are both set to
false
.
- If var is
null
, returnnull
. - If vocab is
true
and var is a key in inverse context:- Initialize default language to
active context's
default language, if it has one, otherwise to
@none
. - If value is a dictionary containing
the property
@preserve
, use the first element from the value of@preserve
as value. - Initialize containers to an empty array. This array will be used to keep track of an ordered list of preferred container mapping for a term, based on what is compatible with value.
- Initialize type/language to
@language
, and type/language value to@null
. These two variables will keep track of the preferred type mapping or language mapping for a term, based on what is compatible with value. - If value is a dictionary,
that contains the key
@index
, and value is not a graph object then append the values@index
and@index@set
to containers. - If reverse is
true
, set type/language to@type
, type/language value to@reverse
, and append@set
to containers. - Otherwise, if value is a list object, then set
type/language and type/language value
to the most specific values that work for all items in
the list as follows:
- If
@index
is a not key in value, then append@list
to containers. - Initialize list to the array associated
with the key
@list
in value. - Initialize common type and common language to
null
. If list is empty, set common language to default language. - For each item in list:
- Initialize item language to
@none
and item type to@none
. - If item contains the key
@value
:- If item contains the key
@language
, then set item language to its associated value. - Otherwise, if item contains the key
@type
, set item type to its associated value. - Otherwise, set item language to
@null
.
- If item contains the key
- Otherwise, set item type to
@id
. - If common language is
null
, set it to item language. - Otherwise, if item language does not equal
common language and item contains the
key
@value
, then set common language to@none
because list items have conflicting languages. - If common type is
null
, set it to item type. - Otherwise, if item type does not equal
common type, then set common type
to
@none
because list items have conflicting types. - If common language is
@none
and common type is@none
, then stop processing items in the list because it has been detected that there is no common language or type amongst the items.
- Initialize item language to
- If common language is
null
, set it to@none
. - If common type is
null
, set it to@none
. - If common type is not
@none
then set type/language to@type
and type/language value to common type. - Otherwise, set type/language value to common language.
- If
- Otherwise, if value is a graph object,
prefer a mapping most appropriate for the particular value.
- If value contains the key
@index
, append the values@graph@index
and@graph@index@set
to containers. - If the value contains the key
@id
, append the values@graph@id
and@graph@id@set
to containers. - Append the values
@graph
@graph@set
, and@set
to containers. - If value does not contain the key
@index
, append the values@graph@index
and@graph@index@set
to containers. - If the value does not contain the key
@id
, append the values@graph@id
and@graph@id@set
to containers. - Append the values
@index
and@index@set
to containers.
- If value contains the key
- Otherwise:
- If value is a value object:
- If value contains the key
@language
and does not contain the key@index
, then set type/language value to its associated value and, append@language
and@language@set
to containers. - Otherwise, if value contains the key
@type
, then set type/language value to its associated value and set type/language to@type
.
- If value contains the key
- Otherwise, set type/language to
@type
and set type/language value to@id
, and append@id
,@id@set
,@type
, and@set@type
, to containers. - Append
@set
to containers.
- If value is a value object:
- Append
@none
to containers. This represents the non-existence of a container mapping, and it will be the last container mapping value to be checked as it is the most generic. -
If processing mode is
json-ld-1.1
and value does not contain the key@index
, append@index
and@index@set
to containers. -
If processing mode is
json-ld-1.1
and value contains only the key@value
, append@language
and@language@set
to containers. - If type/language value is
null
, set it to@null
. This is the key under whichnull
values are stored in the inverse context entry. - Initialize preferred values to an empty array. This array will indicate, in order, the preferred values for a term's type mapping or language mapping.
- If type/language value is
@reverse
, append@reverse
to preferred values. - If type/language value is
@id
or@reverse
and value has an@id
member:- If the result of using the
IRI compaction algorithm,
passing active context, inverse context,
the value associated with the
@id
key in value for var, andtrue
for vocab has a term definition in the active context with an IRI mapping that equals the value associated with the@id
key in value, then append@vocab
,@id
, and@none
, in that order, to preferred values. - Otherwise, append
@id
,@vocab
, and@none
, in that order, to preferred values.
- If the result of using the
IRI compaction algorithm,
passing active context, inverse context,
the value associated with the
- Otherwise, append type/language value and
@none
, in that order, to preferred values. If value is an empty list object, set type/language to@any
. - Initialize term to the result of the Term Selection algorithm, passing inverse context, var, containers, type/language, and preferred values.
- If term is not
null
, return term.
- Initialize default language to
active context's
default language, if it has one, otherwise to
- At this point, there is no simple term that var
can be compacted to. If vocab is
true
and active context has a vocabulary mapping:- If var begins with the vocabulary mapping's value but is longer, then initialize suffix to the substring of var that does not match. If suffix does not have a term definition in active context, then return suffix.
- The var could not be compacted using the
active context's vocabulary mapping.
Try to create a compact IRI, starting by initializing
compact IRI to
null
. This variable will be used to tore the created compact IRI, if any. - For each key term and value term definition in
the active context:
- If the term definition is
null
, its IRI mapping equals var, its IRI mapping is not a substring at the beginning of var, or the term definition does not contain the prefix flag having a value oftrue
, the term cannot be used as a prefix. Continue with the next term. - Initialize candidate by concatenating term,
a colon (
:
), and the substring of var that follows after the value of the term definition's IRI mapping. - If either compact IRI is
null
, candidate is shorter or the same length but lexicographically less than compact IRI and candidate does not have a term definition in active context, or if the term definition has an IRI mapping that equals var and value isnull
, set compact IRI to candidate.
- If the term definition is
- If compact IRI is not
null
, return compact IRI. - If vocab is
false
, transform var to a relative IRI using the base IRI from active context, if it exists. - Finally, return var as is.
6.4 Term Selection §
This algorithm, invoked via the IRI Compaction algorithm, makes use of an active context's inverse context to find the term that is best used to compact an IRI. Other information about a value associated with the IRI is given, including which container mapping and which type mapping or language mapping would be best used to express the value.
6.4.1 Overview §
This section is non-normative.
The inverse context's entry for the IRI will be first searched according to the preferred container mapping, in the order that they are given. Amongst terms with a matching container mapping, preference will be given to those with a matching type mapping or language mapping, over those without a type mapping or language mapping. If there is no term with a matching container mapping then the term without a container mapping that matches the given type mapping or language mapping is selected. If there is still no selected term, then a term with no type mapping or language mapping will be selected if available. No term will be selected that has a conflicting type mapping or language mapping. Ties between terms that have the same mappings are resolved by first choosing the shortest terms, and then by choosing the lexicographically least term. Note that these ties are resolved automatically because they were previously resolved when the Inverse Context Creation algorithm was used to create the inverse context.
6.4.2 Algorithm §
This algorithm has five required inputs. They are: an inverse context, a keyword or IRI var, an array containers that represents an ordered list of preferred container mapping, a string type/language that indicates whether to look for a term with a matching type mapping or language mapping, and an array representing an ordered list of preferred values for the type mapping or language mapping to look for.
- Initialize container map to the value associated with var in the inverse context.
- For each item container in containers:
- If container is not a key in container map, then there is no term with a matching container mapping for it, so continue to the next container.
- Initialize type/language map to the value associated with the container member in container map.
- Initialize value map to the value associated with type/language member in type/language map.
- For each item in preferred values:
- If item is not a key in value map, then there is no term with a matching type mapping or language mapping, so continue to the next item.
- Otherwise, a matching term has been found, return the value associated with the item member in value map.
- No matching term has been found. Return
null
.
6.4.3 Examples §
This section is non-normative.
The following examples are intended to illustrate how the term selection algorithm behaves for different term definitions and values. It is not comprehensive, but intended to illustrate different parts of the algorithm.
6.4.3.1 Language Map Term §
If the term definition has "@container": "@language"
, it will only match a
value object having no @type
.
{
"@context": {"t": {"@id": "https://example/t", "@container": "@language"}}
}
The inverse context will contain the following:
{
"@language": {
"@language": {"@none": "t"},
"@type": {"@none": "t"},
"@any": {"@none": "t"}
}
}
6.4.3.2 Datatyped Term §
If the term definition has a datatype, it will only match a value object having a matching datatype.
{
"@context": {"t": {"@id": "https://example/t", "@type": "http:/example/type"}}
}
The inverse context will contain the following:
{
"@none": {
"@language": {},
"@type": {"http:/example/type": "t"},
"@any": {"@none": "t"}
}
}
6.5 Value Compaction §
Expansion transforms all values into expanded form in JSON-LD. This algorithm performs the opposite operation, transforming a value into compacted form. This algorithm compacts a value according to the term definition in the given active context that is associated with the value's associated active property.
6.5.1 Overview §
This section is non-normative.
The value to compact has either an @id
or an
@value
member.
For the former case, if the type mapping of
active property is set to @id
or @vocab
and value consists of only an @id
member and, if
the container mapping of active property
includes @index
, an @index
member, value
can be compacted to a string by returning the result of
using the IRI Compaction algorithm
to compact the value associated with the @id
member.
Otherwise, value cannot be compacted and is returned as is.
For the latter case, it might be possible to compact value
just into the value associated with the @value
member.
This can be done if the active property has a matching
type mapping or language mapping and there
is either no @index
member or the container mapping
of active property includes @index
. It can
also be done if @value
is the only member in value
(apart an @index
member in case the container mapping
of active property includes @index
) and
either its associated value is not a string, there is
no default language, or there is an explicit
null
language mapping for the
active property.
6.5.2 Algorithm §
This algorithm has four required inputs: an active context, an inverse context, an active property, and a value to be compacted.
- Initialize number members to the number of members value contains.
- If value has an
@index
member and the container mapping associated to active property includes@index
, decrease number members by1
. - If number members is greater than
2
, return value as it cannot be compacted. - If value has an
@id
member:- If number members is
1
and the type mapping of active property is set to@id
, return the result of using the IRI compaction algorithm, passing active context, inverse context, and the value of the@id
member for var. - Otherwise, if number members is
1
and the type mapping of active property is set to@vocab
, return the result of using the IRI compaction algorithm, passing active context, inverse context, the value of the@id
member for var, andtrue
for vocab. - Otherwise, return value as is.
- If number members is
- Otherwise, if value has an
@type
member whose value matches the type mapping of active property, return the value associated with the@value
member of value. - Otherwise, if value has an
@language
member whose value matches the language mapping of active property, return the value associated with the@value
member of value. - Otherwise, if number members equals
1
and either the value of the@value
member is not a string, or the active context has no default language, or the language mapping of active property is set tonull
,, return the value associated with the@value
member. - Otherwise, return value as is.
7. Flattening Algorithms §
7.1 Flattening Algorithm §
This algorithm flattens an expanded JSON-LD document by collecting all properties of a node in a single dictionary and labeling all blank nodes with blank node identifiers. This resulting uniform shape of the document, may drastically simplify the code required to process JSON-LD data in certain applications.
7.1.1 Overview §
This section is non-normative.
First, a node map is generated using the Node Map Generation algorithm which collects all properties of a node in a single dictionary. In the next step, the node map is converted to a JSON-LD document in flattened document form. Finally, if a context has been passed, the flattened document is compacted using the Compaction algorithm before being returned.
7.1.2 Algorithm §
The algorithm takes two input variables, an element to flatten and
an optional context used to compact the flattened document. If not
passed, context is set to null
.
This algorithm generates new blank node identifiers
and relabels existing blank node identifiers.
The Generate Blank Node Identifier algorithm
keeps an identifier map and a counter to ensure consistent
relabeling and avoid collisions. Thus, before this algorithm is run,
the identifier map is reset and the counter is initialized
to 0
.
- Initialize node map to a dictionary consisting of
a single member whose key is
@default
and whose value is an empty dictionary. - Perform the Node Map Generation algorithm, passing element and node map.
- Initialize default graph to the value of the
@default
member of node map, which is a dictionary representing the default graph. - For each key-value pair graph name-graph in node map
where graph name is not
@default
, perform the following steps:- If default graph does not have a graph name member, create
one and initialize its value to a dictionary consisting of an
@id
member whose value is set to graph name. - Reference the value associated with the graph name member in default graph using the variable entry.
- Add an
@graph
member to entry and set it to an empty array. - For each id-node pair in graph ordered by id,
add node to the
@graph
member of entry, unless the only member of node is@id
.
- If default graph does not have a graph name member, create
one and initialize its value to a dictionary consisting of an
- Initialize an empty array flattened.
- For each id-node pair in default graph ordered by id,
add node to flattened,
unless the only member of node is
@id
. - If context is
null
, return flattened. - Otherwise, return the result of compacting flattened according the
Compaction algorithm passing context
ensuring that the compaction result has only the
@graph
keyword (or its alias) at the top-level other than@context
, even if the context is empty or if there is only one element to put in the@graph
array. This ensures that the returned document has a deterministic structure.
7.2 Node Map Generation §
This algorithm creates a dictionary node map holding an indexed
representation of the graphs and nodes
represented in the passed expanded document. All nodes that are not
uniquely identified by an IRI get assigned a (new) blank node identifier.
The resulting node map will have a member for every graph in the document whose
value is another object with a member for every node represented in the document.
The default graph is stored under the @default
member, all other graphs are
stored under their graph name.
7.2.1 Overview §
This section is non-normative.
The algorithm recursively runs over an expanded JSON-LD document to
collect all properties of a node
in a single dictionary. The algorithm constructs a
dictionary node map whose keys represent the
graph names used in the document
(the default graph is stored under the key @default
)
and whose associated values are dictionaries
which index the nodes in the
graph. If a
property's value is a node object,
it is replaced by a node object consisting of only an
@id
member. If a node object has no @id
member or it is identified by a blank node identifier,
a new blank node identifier is generated. This relabeling
of blank node identifiers is
also done for properties and values of
@type
.
7.2.2 Algorithm §
The algorithm takes as input an expanded JSON-LD document element and a reference to
a dictionary node map. Furthermore it has the optional parameters
active graph (which defaults to @default
), an active subject,
active property, and a reference to a dictionary list. If
not passed, active subject, active property, and list are
set to null
.
- If element is an array, process each item in element
as follows and then return:
- Run this algorithm recursively by passing item for element, node map, active graph, active subject, active property, and list.
- Otherwise element is a dictionary. Reference the
dictionary which is the value of the active graph
member of node map using the variable graph. If the
active subject is
null
, set node tonull
otherwise reference the active subject member of graph using the variable node. - If element has an
@type
member, perform for each item the following steps:- If item is a blank node identifier, replace it with a newly generated blank node identifier passing item for identifier.
- If element has an
@value
member, perform the following steps:- If list is
null
:- If node does not have an active property member, create one and initialize its value to an array containing element.
- Otherwise, compare element against every item in the array associated with the active property member of node. If there is no item equivalent to element, append element to the array. Two dictionaries are considered equal if they have equivalent key-value pairs.
- Otherwise, append element to the
@list
member of list.
- If list is
- Otherwise, if element has an
@list
member, perform the following steps:- Initialize a new dictionary result consisting of a single member
@list
whose value is initialized to an empty array. - Recursively call this algorithm passing the value of element's
@list
member for element, active graph, active subject, active property, and result for list. - Append result to the value of the active property member of node.
- Initialize a new dictionary result consisting of a single member
- Otherwise element is a node object, perform
the following steps:
- If element has an
@id
member, set id to its value and remove the member from element. If id is a blank node identifier, replace it with a newly generated blank node identifier passing id for identifier. - Otherwise, set id to the result of the
Generate Blank Node Identifier algorithm
passing
null
for identifier. - If graph does not contain a member id, create one and initialize
its value to a dictionary consisting of a single member
@id
whose value is id. - Reference the value of the id member of graph using the variable node.
- If active subject is a dictionary, a reverse property relationship
is being processed. Perform the following steps:
- If node does not have an active property member, create one and initialize its value to an array containing active subject.
- Otherwise, compare active subject against every item in the array associated with the active property member of node. If there is no item equivalent to active subject, append active subject to the array. Two dictionaries are considered equal if they have equivalent key-value pairs.
- Otherwise, if active property is not
null
, perform the following steps:- Create a new dictionary reference consisting of a single member
@id
whose value is id. - If list is
null
:- If node does not have an active property member, create one and initialize its value to an array containing reference.
- Otherwise, compare reference against every item in the array associated with the active property member of node. If there is no item equivalent to reference, append reference to the array. Two dictionaries are considered equal if they have equivalent key-value pairs.
- Otherwise, append reference to the
@list
member of list.
- Create a new dictionary reference consisting of a single member
- If element has an
@type
key, append each item of its associated array to the array associated with the@type
key of node unless it is already in that array. Finally remove the@type
member from element. - If element has an
@index
member, set the@index
member of node to its value. If node has already an@index
member with a different value, aconflicting indexes
error has been detected and processing is aborted. Otherwise, continue by removing the@index
member from element. - If element has an
@reverse
member:- Create a dictionary referenced node with a single member
@id
whose value is id. - Set reverse map to the value of the
@reverse
member of element. - For each key-value pair property-values in reverse map:
- For each value of values:
- Recursively invoke this algorithm passing value for element, node map, active graph, referenced node for active subject, and property for active property. Passing a dictionary for active subject indicates to the algorithm that a reverse property relationship is being processed.
- For each value of values:
- Remove the
@reverse
member from element.
- Create a dictionary referenced node with a single member
- If element has an
@graph
member, recursively invoke this algorithm passing the value of the@graph
member for element, node map, and id for active graph before removing the@graph
member from element. - Finally, for each key-value pair property-value in element ordered by
property perform the following steps:
- If property is a blank node identifier, replace it with a newly generated blank node identifier passing property for identifier.
- If node does not have a property member, create one and initialize its value to an empty array.
- Recursively invoke this algorithm passing value for element, node map, active graph, id for active subject, and property for active property.
- If element has an
7.3 Generate Blank Node Identifier §
This algorithm is used to generate new blank node identifiers or to relabel an existing blank node identifier to avoid collision by the introduction of new ones.
7.3.1 Overview §
This section is non-normative.
The simplest case is if there exists already a blank node identifier
in the identifier map for the passed identifier, in which
case it is simply returned. Otherwise, a new blank node identifier
is generated by concatenating the string _:b
and the
counter. If the passed identifier is not null
,
an entry is created in the identifier map associating the
identifier with the blank node identifier. Finally,
the counter is increased by one and the new
blank node identifier is returned.
7.3.2 Algorithm §
The algorithm takes a single input variable identifier which may
be null
. Between its executions, the algorithm needs to
keep an identifier map to relabel existing
blank node identifiers
consistently and a counter to generate new
blank node identifiers. The
counter is initialized to 0
by default.
- If identifier is not
null
and has an entry in the identifier map, return the mapped identifier. - Otherwise, generate a new blank node identifier by concatenating
the string
_:b
and counter. - Increment counter by
1
. - If identifier is not
null
, create a new entry for identifier in identifier map and set its value to the new blank node identifier. - Return the new blank node identifier.
7.4 Merge Node Maps §
This algorithm creates a new map of subjects to nodes using all graphs contained in the graph map created using the Node Map Generation algorithm to create merged node objects containing information defined for a given subject in each graph contained in the node map.
- Create result as an empty dictionary
- For each graph name and node map in graph map
and for each id and node in node map:
- Set merged node to the value for id in result, initializing it
with a new dictionary consisting of a single member
@id
whose value is id, if it does not exist. - For each property and values in node:
- Set merged node to the value for id in result, initializing it
with a new dictionary consisting of a single member
- Return result.
8. RDF Serialization/Deserialization Algorithms §
This section describes algorithms to deserialize a JSON-LD document to an RDF dataset and vice versa. The algorithms are designed for in-memory implementations with random access to dictionary elements.
Throughout this section, the following vocabulary prefixes are used in compact IRIs:
Prefix | IRI |
---|---|
rdf | https://www.w3.org/1999/02/22-rdf-syntax-ns# |
rdfs | https://www.w3.org/2000/01/rdf-schema# |
xsd | https://www.w3.org/2001/XMLSchema# |
8.1 Deserialize JSON-LD to RDF algorithm §
This algorithm deserializes a JSON-LD document to an RDF dataset. Please note that RDF does not allow a blank node to be used as a property, while JSON-LD does. Therefore, by default RDF triples that would have contained blank nodes as properties are discarded when interpreting JSON-LD as RDF.
8.1.1 Overview §
This section is non-normative.
The JSON-LD document is expanded and converted to a node map using the
Node Map Generation algorithm.
This allows each graph represented within the document to be
extracted and flattened, making it easier to process each
node object. Each graph from the node map
is processed to extract RDF triple,
to which any (non-default) graph name is applied to create an
RDF dataset. Each node object in the
node map has an @id
member which corresponds to the
RDF subject, the other members
represent RDF predicates. Each
member value is either an IRI or
blank node identifier or can be transformed to an
RDF literal
to generate an RDF triple. Lists
are transformed into an
RDF collection
using the List to RDF Conversion algorithm.
8.1.2 Algorithm §
The algorithm takes a JSON-LD document element and returns an
RDF dataset. Unless the produceGeneralizedRdf
option
is set to true
, RDF triple
containing a blank node predicate
are excluded from output.
This algorithm generates new blank node identifiers
and relabels existing blank node identifiers.
The Generate Blank Node Identifier algorithm
keeps an identifier map and a counter to ensure consistent
relabeling and avoid collisions. Thus, before this algorithm is run,
the identifier map is reset and the counter is initialized
to 0
.
- Expand element according to the Expansion algorithm.
- Generate a node map according to the Node Map Generation algorithm.
- Initialize an empty RDF dataset dataset.
- For each graph name and graph in node map
ordered by graph name:
- If graph name is a relative IRI, continue with the next graph name-graph pair.
- Initialize triples as an empty array.
- For each subject and node in graph ordered
by subject:
- If subject is a relative IRI, continue with the next subject-node pair.
- For each property and values in node
ordered by property:
- If property is
@type
, then for each type in values, append a triple composed of subject,rdf:type
, and type to triples. - Otherwise, if property is a keyword continue with the next property-values pair.
- Otherwise, if property is a blank node identifier and
the
produceGeneralizedRdf
option is nottrue
, continue with the next property-values pair. - Otherwise, if property is a relative IRI, continue with the next property-values pair.
- Otherwise, property is an absolute IRI or
blank node identifier. For each item
in values:
- If item is a list object, initialize
list triples as an empty array and
list head to the result of the List Conversion algorithm, passing
the value associated with the
@list
key from item and list triples. Append first a triple composed of subject, property, and list head to triples and finally append all triples from list triples to triples. - Otherwise, item is a value object
or a node object. Append a triple
composed of subject, property, and
the result of using the
Object to RDF Conversion algorithm
passing item to triples, unless the result is
null
, indicating a relative IRI that has to be ignored.
- If item is a list object, initialize
list triples as an empty array and
list head to the result of the List Conversion algorithm, passing
the value associated with the
- If property is
- If graph name is
@default
, add triples to the default graph in dataset. - Otherwise, create a named graph in dataset composed of graph name and add triples.
- Return dataset.
8.2 Object to RDF Conversion §
This algorithm takes a node object or value object
and transforms it into an
RDF resource
to be used as the object of an RDF triple. If a
node object containing a relative IRI is passed to
the algorithm, null
is returned which then causes the resulting
RDF triple to be ignored.
8.2.1 Overview §
This section is non-normative.
Value objects are transformed to
RDF literals as described in
section 8.6 Data Round Tripping
whereas node objects are transformed
to IRIs,
blank node identifiers,
or null
.
8.2.2 Algorithm §
The algorithm takes as its sole argument item which MUST be either a value object or node object.
- If item is a node object and the value of
its
@id
member is a relative IRI, returnnull
. - If item is a node object, return the
IRI or blank node identifier associated
with its
@id
member. - Otherwise, item is a value object. Initialize
value to the value associated with the
@value
member in item. - Initialize datatype to the value associated with the
@type
member of item ornull
if item does not have such a member. - If value is
true
orfalse
, set value to the stringtrue
orfalse
which is the canonical lexical form as described in section 8.6 Data Round Tripping If datatype isnull
, set it toxsd:boolean
. - Otherwise, if value is a number with a non-zero fractional
part (the result of a modulo‑1 operation) or value is a number
and datatype equals
xsd:double
, convert value to a string in canonical lexical form of anxsd:double
as defined in [XMLSCHEMA11-2] and described in section 8.6 Data Round Tripping. If datatype isnull
, set it toxsd:double
. - Otherwise, if value is a number with no non-zero
fractional part (the result of a modulo‑1 operation) or value
is a number and datatype
equals
xsd:integer
, convert value to a string in canonical lexical form of anxsd:integer
as defined in [XMLSCHEMA11-2] and described in section 8.6 Data Round Tripping. If datatype isnull
, set it toxsd:integer
. - Otherwise, if datatype is
null
, set it toxsd:string
orrdf:langString
, depending on if item has an@language
member. - Initialize literal as an RDF literal using
value and datatype. If item has an
@language
member, add the value associated with the@language
key as the language tag of literal. - Return literal.
8.3 List to RDF Conversion §
List Conversion is the process of taking a list object and transforming it into an RDF collection as defined in RDF Semantics [RDF11-MT].
8.3.1 Overview §
This section is non-normative.
For each element of the list a new blank node identifier
is allocated which is used to generate rdf:first
and
rdf:rest
ABBR. The
algorithm returns the list head, which is either the first allocated
blank node identifier or rdf:nil
if the
list is empty. If a list element represents a relative IRI,
the corresponding rdf:first
triple is omitted.
8.3.2 Algorithm §
The algorithm takes two inputs: an array list and an empty array list triples used for returning the generated triples.
- If list is empty, return
rdf:nil
. - Otherwise, create an array bnodes composed of a newly generated blank node identifier for each entry in list.
- Initialize an empty array list triples.
- For each pair of subject from bnodes and item from list:
- Initialize object to the result of using the Object to RDF Conversion algorithm passing item to list triples.
- Unless object is
null
, append a triple composed of subject,rdf:first
, and object. - Set rest as the next entry in bnodes, or if that
does not exist,
rdf:nil
. Append a triple composed of subject,rdf:rest
, and rest to list triples.
- Return the first blank node from bnodes or
rdf:nil
if bnodes is empty.
8.4 Serialize RDF as JSON-LD Algorithm §
This algorithm serializes an RDF dataset consisting of a default graph and zero or more named graphs into a JSON-LD document.
In the RDF abstract syntax, RDF literals have a lexical form, as defined in [RDF11-CONCEPTS]. The form of these literals is used when creating JSON-LD values based on these literals.
8.4.1 Overview §
This section is non-normative.
Iterate through each graph in the dataset, converting each
RDF collection into a list
and generating a JSON-LD document in expanded form for all
RDF literals, IRIs
and blank node identifiers.
If the use native types flag is set to true
,
RDF literals with a
datatype IRI
that equals xsd:integer
or xsd:double
are converted
to a JSON numbers and RDF literals
with a datatype IRI
that equals xsd:boolean
are converted to true
or
false
based on their
lexical form
as described in
section 8.6 Data Round Tripping.
Unless the use rdf:type
flag is set to true, rdf:type
predicates will be serialized as @type
as long as the associated object is
either an IRI or blank node identifier.
8.4.2 Algorithm §
The algorithm takes one required and two optional inputs: an RDF dataset dataset
and the two flags use native types and use rdf:type
that both default to false
.
- Initialize default graph to an empty dictionary.
- Initialize graph map to a dictionary consisting
of a single member
@default
whose value references default graph. - Initialize node usage map to an empty dictionary.
- For each graph in dataset:
- If graph is the default graph,
set name to
@default
, otherwise to the graph name associated with graph. - If graph map has no name member, create one and set its value to an empty dictionary.
- If graph is not the default graph and
default graph does not have a name member,
create such a member and initialize its value to a new
dictionary with a single member
@id
whose value is name. - Reference the value of the name member in graph map using the variable node map.
- For each RDF triple in graph
consisting of subject, predicate, and object:
- If node map does not have a subject member,
create one and initialize its value to a new dictionary
consisting of a single member
@id
whose value is set to subject. - Reference the value of the subject member in node map using the variable node.
- If object is an IRI or blank node identifier,
and node map does not have an object member,
create one and initialize its value to a new dictionary
consisting of a single member
@id
whose value is set to object. - If predicate equals
rdf:type
, the userdf:type
flag is nottrue
, and object is an IRI or blank node identifier, append object to the value of the@type
member of node; unless such an item already exists. If no such member exists, create one and initialize it to an array whose only item is object. Finally, continue to the next RDF triple. - Set value to the result of using the RDF to Object Conversion algorithm, passing object and use native types.
- If node does not have an predicate member, create one and initialize its value to an empty array.
- If there is no item equivalent to value in the array associated with the predicate member of node, append a reference to value to the array. Two JSON objects are considered equal if they have equivalent key-value pairs.
- If object is a blank node identifier or IRI,
it might represent the list node:
- If the object member of node usage map does not exist, initialize it to a new empty array.
- Append the value of the
@id
member ofnode
to the object member of node usage map. - If the object member of node map has no
usages
member, create one and initialize it to an empty array. - Reference the
usages
member of the object member of node map using the variable usages. - Append a new dictionary consisting of three
members,
node
,property
, andvalue
to the usages array. Thenode
member is set to a reference to node,property
to predicate, andvalue
to a reference to value.
- If node map does not have a subject member,
create one and initialize its value to a new dictionary
consisting of a single member
- If graph is the default graph,
set name to
- For each name and graph object in graph map:
- If graph object has no
rdf:nil
member, continue with the next name-graph object pair as the graph does not contain any lists that need to be converted. - Initialize nil to the value of the
rdf:nil
member of graph object. - For each item usage in the
usages
member of nil, perform the following steps:- Initialize node to the value of the value of the
node
member of usage, property to the value of theproperty
member of usage, and head to the value of thevalue
member of usage. - Initialize two empty arrays list and list nodes.
- While property equals
rdf:rest
, the value of the@id
member of node is a blank node identifier, the array value of the member of node usage map associated with the@id
member ofnode
has only one member, the value associated to theusages
member of node has exactly 1 entry, node has ardf:first
andrdf:rest
property, both of which have as value an array consisting of a single element, and node has no other members apart from an optional@type
member whose value is an array with a single item equal tordf:List
, node represents a well-formed list node. Perform the following steps to traverse the list backwards towards its head:- Append the only item of
rdf:first
member of node to the list array. - Append the value of the
@id
member of node to the list nodes array. - Initialize node usage to the only item of the
usages
member of node. - Set node to the value of the
node
member of node usage, property to the value of theproperty
member of node usage, and head to the value of thevalue
member of node usage. - If the
@id
member of node is an IRI instead of a blank node identifier, exit the while loop.
- Append the only item of
- If property equals
rdf:first
, i.e., the detected list is nested inside another list- and the value of the
@id
of node equalsrdf:nil
, i.e., the detected list is empty, continue with the next usage item. Therdf:nil
node cannot be converted to a list object as it would result in a list of lists, which isn't supported. - Otherwise, the list consists of at least one item. We preserve the head node and transform the rest of the linked list to a list object.
- Set head id to the value of the
@id
member of head. - Set head to the value of the head id member of graph object so that all it's properties can be accessed.
- Then, set head to the only item in the value of the
rdf:rest
member of head. - Finally, remove the last item of the list array and the last item of the list nodes array.
- and the value of the
- Remove the
@id
member from head. - Reverse the order of the list array.
- Add an
@list
member to head and initialize its value to the list array. - For each item node id in list nodes, remove the node id member from graph object.
- Initialize node to the value of the value of the
- If graph object has no
- Initialize an empty array result.
- For each subject and node in default graph
ordered by subject:
- If graph map has a subject member:
- Add an
@graph
member to node and initialize its value to an empty array. - For each key-value pair s-n in the subject
member of graph map ordered by s, append n
to the
@graph
member of node after removing itsusages
member, unless the only remaining member of n is@id
.
- Add an
- Append node to result after removing its
usages
member, unless the only remaining member of node is@id
.
- If graph map has a subject member:
- Return result.
8.5 RDF to Object Conversion §
This algorithm transforms an RDF literal to a JSON-LD value object and a RDF blank node or IRI to an JSON-LD node object.
8.5.1 Overview §
This section is non-normative.
RDF literals are transformed to
value objects whereas IRIs and
blank node identifiers are
transformed to node objects.
If the use native types flag is set to true
,
RDF literals with a
datatype IRI
that equals xsd:integer
or xsd:double
are converted
to a JSON numbers and RDF literals
with a datatype IRI
that equals xsd:boolean
are converted to true
or
false
based on their
lexical form
as described in
section 8.6 Data Round Tripping.
8.5.2 Algorithm §
This algorithm takes two required inputs: a value to be converted to a dictionary and a flag use native types.
- If value is an IRI or a
blank node identifier, return a new dictionary
consisting of a single member
@id
whose value is set to value. - Otherwise value is an
RDF literal:
- Initialize a new empty dictionary result.
- Initialize converted value to value.
- Initialize type to
null
- If use native types is
true
- If the
datatype IRI
of value equals
xsd:string
, set converted value to the lexical form of value. - Otherwise, if the
datatype IRI
of value equals
xsd:boolean
, set converted value totrue
if the lexical form of value matchestrue
, orfalse
if it matchesfalse
. If it matches neither, set type toxsd:boolean
. - Otherwise, if the
datatype IRI
of value equals
xsd:integer
orxsd:double
and its lexical form is a validxsd:integer
orxsd:double
according [XMLSCHEMA11-2], set converted value to the result of converting the lexical form to a JSON number.
- If the
datatype IRI
of value equals
- Otherwise, if value is a
language-tagged string
add a member
@language
to result and set its value to the language tag of value. - Otherwise, set type to the
datatype IRI
of value, unless it equals
xsd:string
which is ignored. - Add a member
@value
to result whose value is set to converted value. - If type is not
null
, add a member@type
to result whose value is set to type. - Return result.
8.6 Data Round Tripping §
When deserializing JSON-LD to RDF
JSON-native numbers are automatically
type-coerced to xsd:integer
or xsd:double
depending on whether the number has a non-zero fractional part
or not (the result of a modulo‑1 operation), the boolean values
true
and false
are coerced to xsd:boolean
,
and strings are coerced to xsd:string
.
The numeric or boolean values themselves are converted to
canonical lexical form, i.e., a deterministic string
representation as defined in [XMLSCHEMA11-2].
The canonical lexical form of an integer, i.e., a
number with no non-zero fractional part or a number
coerced to xsd:integer
, is a finite-length sequence of decimal
digits (0-9
) with an optional leading minus sign; leading
zeros are prohibited. In JavaScript, implementers can use the following
snippet of code to convert an integer to
canonical lexical form:
(value).toFixed(0).toString()
The canonical lexical form of a double, i.e., a
number with a non-zero fractional part or a number
coerced to xsd:double
, consists of a mantissa followed by the
character E
, followed by an exponent. The mantissa is a
decimal number and the exponent is an integer. Leading zeros and a
preceding plus sign (+
) are prohibited in the exponent.
If the exponent is zero, it is indicated by E0
. For the
mantissa, the preceding optional plus sign is prohibited and the
decimal point is required. Leading and trailing zeros 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-2008] whereas
the value space of JSON numbers is not
specified; when deserializing JSON-LD to RDF the mantissa is rounded to
15 digits after the decimal point. In JavaScript, implementers
can use the following snippet of code to convert a double to
canonical lexical form:
(value).toExponential(15).replace(/(\d)0*e\+?/,'$1E')
The canonical lexical form of the boolean
values true
and false
are the strings
true
and false
.
When JSON-native numbers are deserialized
to RDF, lossless data round-tripping cannot be guaranteed, as rounding
errors might occur. When
serializing RDF as JSON-LD,
similar rounding errors might occur. Furthermore, the datatype or the lexical
representation might be lost. An xsd:double
with a value
of 2.0
will, e.g., result in an xsd:integer
with a value of 2
in canonical lexical form
when converted from RDF to JSON-LD and back to RDF. It is important
to highlight that in practice it might be impossible to losslessly
convert an xsd:integer
to a number because
its value space is not limited. While the JSON specification [RFC7159]
does not limit the value space of numbers
either, concrete implementations typically do have a limited value
space.
To ensure lossless round-tripping the
Serialize RDF as JSON-LD algorithm
specifies a use native types flag which controls whether
RDF literals
with a datatype IRI
equal to xsd:integer
, xsd:double
, or
xsd:boolean
are converted to their JSON-native
counterparts. If the use native types flag is set to
false
, all literals remain in their original string
representation.
Some JSON serializers, such as PHP's native implementation in some versions,
backslash-escape the forward slash character. For example, the value
https://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, forward slashes MUST NOT be
backslash-escaped.
9. The Application Programming Interface §
This API provides a clean mechanism that enables developers to convert JSON-LD data into a variety of output formats that are often easier to work with.
The JSON-LD API uses Promises to represent the result of the various asynchronous operations. Promises are defined in [ECMASCRIPT-6.0]. General use within specifications can be found in [promises-guide].
9.1 The JsonLdProcessor
Interface §
The JsonLdProcessor
interface is the high-level programming structure
that developers use to access the JSON-LD transformation methods.
It is important to highlight that implementations do not modify the input parameters.
If an error is detected, the Promise is
rejected passing a JsonLdError
with the corresponding error
code
and processing is stopped.
If the documentLoader
option is specified, it is used to dereference remote documents and contexts.
The documentUrl
in the returned RemoteDocument
is used as base IRI and the
contextUrl
is used instead of looking at the HTTP Link Header directly. For the sake of simplicity, none of the algorithms
in this document mention this directly.
[Constructor]
interface JsonLdProcessor
{
static Promise<JsonLdDictionary
> compact
(JsonLdInput
input,
JsonLdContext
context,
optional JsonLdOptions
? options);
static Promise<sequence<JsonLdDictionary
>> expand
(JsonLdInput
input,
optional JsonLdOptions
? options);
static Promise<JsonLdDictionary
> flatten
(JsonLdInput
input,
optional JsonLdContext
? context,
optional JsonLdOptions
? options);
};
compact
-
Compacts the given input using the context according to the steps in the Compaction algorithm:
- Create a new Promise promise and return it. The following steps are then executed asynchronously.
- Set expanded input to the result of using the
expand
method using input and options. - If context is a dictionary having an
@context
member, set context to that member's value, otherwise to context. - Initialize an active context using context;
the base IRI is set to
the
base
option from options, if set; otherwise, if thecompactToRelative
option is true, to the IRI of the currently being processed document, if available; otherwise tonull
. - Set compacted output to the result of using the
Compaction algorithm, using active context,
an empty dictionary as inverse context,
null
as property, expanded input as element, and if passed, thecompactArrays
flag in options. - Fulfill the promise passing compacted output. transforming compacted output from the internal representation to a JSON serialization.
- input
- The dictionary, array of dictionaries to perform the compaction upon or an IRI referencing the JSON-LD document to compact.
- context
- The context to use when compacting the
input
; it can be specified by using a dictionary, an IRI, or an array consisting of dictionaries and IRIs. - options
- A set of options to configure the algorithms. This allows, e.g., to set the input document's base IRI.
expand
-
Expands the given input according to the steps in the Expansion algorithm:
- Create a new Promise promise and return it. The following steps are then executed asynchronously.
- If the passed input is a string
representing the IRI of a remote document, dereference it.
If the retrieved document's content type is neither
application/json
, norapplication/ld+json
, nor any other media type using a+json
suffix as defined in [RFC6839], reject the promise passing anloading document failed
error. - Initialize a new empty active context. The base IRI
of the active context is set to the IRI of the currently being processed
document, if available; otherwise to
null
. If set, thebase
option from options overrides the base IRI. - If an
expandContext
option has been passed, update the active context using the Context Processing algorithm, passing theexpandContext
as local context. IfexpandContext
is a dictionary having an@context
member, pass that member's value instead. - Once input has been retrieved, the response has an HTTP Link Header [RFC5988]
using the
https://www.w3.org/ns/json-ld#context
link relation and a content type ofapplication/json
or any media type with a+json
suffix as defined in [RFC6839] exceptapplication/ld+json
, update the active context using the Context Processing algorithm, passing the context referenced in the HTTP Link Header as local context. The HTTP Link Header is ignored for documents served asapplication/ld+json
If multiple HTTP Link Headers using thehttps://www.w3.org/ns/json-ld#context
link relation are found, the promise is rejected with aJsonLdError
whose code is set tomultiple context link headers
and processing is terminated. - If necessary, transform input into the
internal representation. If input cannot be transformed to the
internal representation, reject promise passing a
loading document failed
error. - Set expanded output to the result of using the
Expansion algorithm, passing the
active context and input as element,
and, if the
frameExpansion
option is set, pass the frame expansion flag astrue
.. - Fulfill the promise passing expanded output. transforming expanded output from the internal representation to a JSON serialization.
- input
- The dictionary or array of dictionaries to perform the expansion upon or an IRI referencing the JSON-LD document to expand.
- options
- A set of options to configure the used algorithms such. This allows, e.g., to set the input document's base IRI.
flatten
-
Flattens the given input and compacts it using the passed context according to the steps in the Flattening algorithm:
- Create a new Promise promise and return it. The following steps are then executed asynchronously.
- Set expanded input to the result of using the
expand
method using input and options. - If context is a dictionary having an
@context
member, set context to that member's value, otherwise to context. - Initialize an active context using context;
the base IRI is set to
the
base
option from options, if set; otherwise, if thecompactToRelative
option is true, to the IRI of the currently being processed document, if available; otherwise tonull
. - Initialize an empty identifier map and a counter (set to
0
) to be used by the Generate Blank Node Identifier algorithm. - Set flattened output to the result of using the
Flattening algorithm, passing
expanded input as element, active context, and if passed, the
compactArrays
flag in options (which is internally passed to the Compaction algorithm). - Fulfill the promise passing flattened output. transforming flattened output from the internal representation to a JSON serialization.
- input
- The dictionary or array of dictionaries or an IRI referencing the JSON-LD document to flatten.
- context
- The context to use when compacting the flattened expanded input;
it can be specified by using a dictionary, an
IRI, or an array consisting of dictionaries
and IRIs. If not
passed or
null
is passed, the result will not be compacted but kept in expanded form. - options
- A set of options to configure the used algorithms such. This allows, e.g., to set the input document's base IRI.
dictionary JsonLdDictionary
{
};
The JsonLdDictionary
is the definition of a dictionary
used to contain arbitrary key/value pairs which are the result of
parsing a JSON Object.
typedef (JsonLdDictionary
or sequence<JsonLdDictionary
> or USVString) JsonLdInput
;
The JsonLdInput
type is used to refer to an input value that
that may be a dictionary, an array of dictionaries or a string representing an
IRI which an be dereferenced to retrieve a valid JSON document.
typedef (JsonLdDictionary
or USVString or sequence<(JsonLdDictionary
or USVString)>) JsonLdContext
;
The JsonLdContext
type is used to refer to a value that
that may be a dictionary, a string representing an
IRI, or an array of dictionaries
and strings.
9.2 The JsonLdOptions Type §
The JsonLdOptions
type is used to pass various options to the
JsonLdProcessor
methods.
dictionary JsonLdOptions
{
USVString? base
;
boolean compactArrays
= true;
boolean compactToRelative
= true;
LoadDocumentCallback
documentLoader
= null;
(JsonLdDictionary
? or USVString) expandContext
= null;
boolean frameExpansion
= false;
USVString processingMode
= null;
boolean produceGeneralizedRdf
= true;
};
base
- The base IRI to use when expanding or compacting the document. If set, this overrides the input document's IRI.
compactArrays
- If set to
true
, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set tofalse
, all arrays will remain arrays even if they have just one element. compactToRelative
- Determines if IRIs are compacted relative to the
base
option or document location when compacting. documentLoader
- The callback of the loader to be used to retrieve remote documents and contexts. If specified, it is used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader is used.
expandContext
- A context that is used to initialize the active context when expanding a document.
frameExpansion
- Enables special frame processing rules for the Expansion Algorithm.
processingMode
- Sets the processing mode.
If set to
json-ld-1.0
orjson-ld-1.1
, the implementation must produce exactly the same results as the algorithms defined in this specification. If set to another value, the JSON-LD processor is allowed to extend or modify the algorithms defined in this specification to enable application-specific optimizations. The definition of such optimizations is beyond the scope of this specification and thus not defined. Consequently, different implementations may implement different optimizations. Developers must not define modes beginning withjson-ld
as they are reserved for future versions of this specification. produceGeneralizedRdf
- If set to
true
, the JSON-LD processor may emit blank nodes for triple predicates, otherwise they will be omitted.
9.3 Remote Document and Context Retrieval §
Users of an API implementation can utilize a callback to control how remote documents and contexts are retrieved. This section details the parameters of that callback and the data structure used to return the retrieved context.
9.3.1 LoadDocumentCallback §
The LoadDocumentCallback
defines a callback that custom document loaders
have to implement to be used to retrieve remote documents and contexts.
callback LoadDocumentCallback
= Promise<USVString> (USVString url);
- url
- The URL of the remote document or context to load.
All errors result in the Promise being rejected with
a JsonLdError
whose code is set to
loading document failed
or multiple context link headers
as described in the next section.
9.3.2 RemoteDocument §
The RemoteDocument
type is used by a LoadDocumentCallback
to return information about a remote document or context.
dictionary RemoteDocument
{
USVString contextUrl
= null;
USVString documentUrl
;
any document
;
};
contextUrl
- If available, the value of the HTTP Link Header [RFC5988] using the
https://www.w3.org/ns/json-ld#context
link relation in the response. If the response's content type isapplication/ld+json
, the HTTP Link Header is ignored. If multiple HTTP Link Headers using thehttps://www.w3.org/ns/json-ld#context
link relation are found, the Promise of theLoadDocumentCallback
is rejected with aJsonLdError
whose code is set tomultiple context link headers
. documentUrl
- The final URL of the loaded document. This is important to handle HTTP redirects properly.
document
- The retrieved document. This can either be the raw payload or the already parsed document.
9.4 Error Handling §
This section describes the datatype definitions used within the JSON-LD API for error handling.
9.4.1 JsonLdError §
The JsonLdError
type is used to report processing errors.
dictionary JsonLdError
{
JsonLdErrorCode
code
;
USVString? message
= null;
};
code
- a string representing the particular error type, as described in the various algorithms in this document.
message
- an optional error message containing additional debugging information. The specific contents of error messages are outside the scope of this specification.
9.4.2 JsonLdErrorCode §
The JsonLdErrorCode
represents the collection of valid JSON-LD error
codes.
enum JsonLdErrorCode
{
"colliding keywords",
"compaction to list of lists",
"conflicting indexes",
"cyclic IRI mapping",
"invalid @id value",
"invalid @index value",
"invalid @nest value",
"invalid @prefix value",
"invalid @reverse value",
"invalid @version value",
"invalid base IRI",
"invalid container mapping",
"invalid default language",
"invalid IRI mapping",
"invalid keyword alias",
"invalid language map value",
"invalid language mapping",
"invalid language-tagged string",
"invalid language-tagged value",
"invalid local context",
"invalid remote context",
"invalid reverse property",
"invalid reverse property map",
"invalid reverse property value",
"invalid scoped context",
"invalid set or list object",
"invalid term definition",
"invalid type mapping",
"invalid type value",
"invalid typed value",
"invalid value object",
"invalid value object value",
"invalid vocab mapping",
"keyword redefinition",
"list of lists",
"loading document failed",
"loading remote context failed",
"multiple context link headers",
"processing mode conflict",
"recursive context inclusion"
};
colliding keywords
- Two properties which expand to the same keyword have been detected. This might occur if a keyword and an alias thereof are used at the same time.
compaction to list of lists
- The compacted document contains a list of lists as multiple lists have been compacted to the same term.
conflicting indexes
- Multiple conflicting indexes have been found for the same node.
cyclic IRI mapping
- A cycle in IRI mappings has been detected.
invalid @id value
- An
@id
member was encountered whose value was not a string. invalid @index value
- An
@index
member was encountered whose value was not a string. invalid @nest value
- An invalid value for
@nest
has been found. invalid @prefix value
- An invalid value for
@prefix
has been found. invalid @reverse value
- An invalid value for an
@reverse
member has been detected, i.e., the value was not a dictionary. invalid @version value
- The
@version
key was used in a context with an out of range value. invalid base IRI
- An invalid base IRI has been detected, i.e., it is
neither an absolute IRI nor
null
. invalid container mapping
- An
@container
member was encountered whose value was not one of the following strings:@list
,@set
, or@index
. invalid default language
- The value of the default language is not a string
or
null
and thus invalid. invalid IRI mapping
- A local context contains a term that has an invalid or missing IRI mapping.
invalid keyword alias
- An invalid keyword alias definition has been encountered.
invalid language map value
- An invalid value in a language map has been detected. It has to be a string or an array of strings.
invalid language mapping
- An
@language
member in a term definition was encountered whose value was neither a string nornull
and thus invalid. invalid language-tagged string
- A language-tagged string with an invalid language value was detected.
invalid language-tagged value
- A number,
true
, orfalse
with an associated language tag was detected. invalid local context
- In invalid local context was detected.
invalid remote context
- No valid context document has been found for a referenced, remote context.
invalid reverse property
- An invalid reverse property definition has been detected.
invalid reverse property map
- An invalid reverse property map has been detected. No
keywords apart from
@context
are allowed in reverse property maps. invalid reverse property value
- An invalid value for a reverse property has been detected. The value of an inverse property must be a node object.
invalid scoped context
- The local context defined within a term definition is invalid.
invalid set or list object
- A set object or list object with disallowed members has been detected.
invalid term definition
- An invalid term definition has been detected.
invalid type mapping
- An
@type
member in a term definition was encountered whose value could not be expanded to an absolute IRI. invalid type value
- An invalid value for an
@type
member has been detected, i.e., the value was neither a string nor an array of strings. invalid typed value
- A typed value with an invalid type was detected.
invalid value object
- A value object with disallowed members has been detected.
invalid value object value
- An invalid value for the
@value
member of a value object has been detected, i.e., it is neither a scalar nornull
. invalid vocab mapping
- An invalid vocabulary mapping has been detected, i.e.,
it is neither an absolute IRI nor
null
. keyword redefinition
- A keyword redefinition has been detected.
list of lists
- A list of lists was detected. List of lists are not supported in this version of JSON-LD due to the algorithmic complexity.
loading document failed
- The document could not be loaded or parsed as JSON.
loading remote context failed
- There was a problem encountered loading a remote context.
multiple context link headers
- Multiple HTTP Link Headers [RFC5988] using the
https://www.w3.org/ns/json-ld#context
link relation have been detected. processing mode conflict
- An attempt was made to change the processing mode which is incompatible with the previous specified version.
recursive context inclusion
- A cycle in remote context inclusions has been detected.
A. IDL Index §
This section is non-normative.
[Constructor] interfaceJsonLdProcessor
{ static Promise<JsonLdDictionary
>compact
(JsonLdInput
input,JsonLdContext
context, optionalJsonLdOptions
? options); static Promise<sequence<JsonLdDictionary
>>expand
(JsonLdInput
input, optionalJsonLdOptions
? options); static Promise<JsonLdDictionary
>flatten
(JsonLdInput
input, optionalJsonLdContext
? context, optionalJsonLdOptions
? options); }; dictionaryJsonLdDictionary
{ }; typedef (JsonLdDictionary
or sequence<JsonLdDictionary
> or USVString)JsonLdInput
; typedef (JsonLdDictionary
or USVString or sequence<(JsonLdDictionary
or USVString)>)JsonLdContext
; dictionaryJsonLdOptions
{ USVString?base
; booleancompactArrays
= true; booleancompactToRelative
= true;LoadDocumentCallback
documentLoader
= null; (JsonLdDictionary
? or USVString)expandContext
= null; booleanframeExpansion
= false; USVStringprocessingMode
= null; booleanproduceGeneralizedRdf
= true; }; callbackLoadDocumentCallback
= Promise<USVString> (USVString url); dictionaryRemoteDocument
{ USVStringcontextUrl
= null; USVStringdocumentUrl
; anydocument
; }; dictionaryJsonLdError
{JsonLdErrorCode
code
; USVString?message
= null; }; enumJsonLdErrorCode
{ "colliding keywords", "compaction to list of lists", "conflicting indexes", "cyclic IRI mapping", "invalid @id value", "invalid @index value", "invalid @nest value", "invalid @prefix value", "invalid @reverse value", "invalid @version value", "invalid base IRI", "invalid container mapping", "invalid default language", "invalid IRI mapping", "invalid keyword alias", "invalid language map value", "invalid language mapping", "invalid language-tagged string", "invalid language-tagged value", "invalid local context", "invalid remote context", "invalid reverse property", "invalid reverse property map", "invalid reverse property value", "invalid scoped context", "invalid set or list object", "invalid term definition", "invalid type mapping", "invalid type value", "invalid typed value", "invalid value object", "invalid value object value", "invalid vocab mapping", "keyword redefinition", "list of lists", "loading document failed", "loading remote context failed", "multiple context link headers", "processing mode conflict", "recursive context inclusion" };
B. Security Considerations §
Consider requirements from Self-Review Questionnaire: Security and Privacy.
C. Changes since 1.0 Recommendation of 16 January 2014 §
This section is non-normative.
- The Expansion Algorithm has a special processing mode, based on the frame expansion flag, to enable content associated with JSON-LD frames, which may not otherwise be valid JSON-LD documents.
- An expanded term definition can now have an
@context
property, which defines a context used for values of a property identified with such a term. This context is used in both the Expansion Algorithm and Compaction Algorithm. - A new section 7.4 Merge Node Maps is required for framing, to create a single graph from the default and named graphs.
- An expanded term definition can now have an
@nest
property, which identifies a term expanding to@nest
which is used for containing properties using the same@nest
mapping. When expanding, the values of a property expanding to@nest
are treated as if they were contained within the enclosing node object directly. @container
values within an expanded term definition may now include@id
and@type
, corresponding to id maps and type maps.- Both language maps and index maps may legitimately have an
@none
value, but JSON-LD 1.0 only allowed string values. This has been updated to allow (and ignore)@none
values. - The JSON syntax has been abstracted into an internal representation to allow for other serialization formats that are functionally equivalent to JSON.
- Preserved values are compacted using the properties of the referencing term.
- The value for
@container
in an expanded term definition can also be an array containing any appropriate container keyword along with@set
(other than@list
). This allows a way to ensure that such property values will always be expressed in array form. - Added support for the
compactToRelative
option to allow IRI compaction (section 6.3 IRI Compaction) to document relative IRIs to be disabled. - In JSON-LD 1.1, terms will be used as compact IRI prefixes
when compacting only if
a simple term definition is used where the value ends with a URI gen-delim character,
or if their expanded term definition contains
a
@prefix
member with the value true. The 1.0 algorithm has been updated to only consider terms that map to a value that ends with a URI gen-delim character. - Term definitions now allow
@container
to include@graph
, along with@id
,@index
and@set
. In the Expansion Algorithm, this is used to create a named graph from either a node object, or objects which are values of keys in an id map or index map. the Compaction Algorithm allows specific forms of graph objects to be compacted back to a set of node objects, or maps of node objects. - Value Expansion will not turn native values into node objects.
- The Term Selection algorithm has been
updated to allow uses of containers for values which would otherwise not
match. This is used in the Compaction
Algorithm to use the
@none
keyword, or an alias, for values of maps for which there is no natural index. The Expansion Algorithm removes this indexing transparently. - The empty string (
""
) has been added as a possible value for@vocab
in a context. When this is set, vocabulary-relative IRIs, such as the keys of node objects, are expanded or compacted relative to the base IRI using string concatenation.
D. Open Issues §
This section is non-normative.
The following is a list of issues open at the time of publication.
Thanks for the great work with JSON-LD! However, when trying to use JSON-LD for to present data in the company I'm working in, I noticed the following missing feature:
FEATURE PROPOSAL: ABILITY TO DEFINE ANY KEY AS AN INDEX KEY
In addition to JSON-LD's existing index container structure, I propose that any key under a JSON-LD node could be defined as a index key.
This would help clustering data under a node into coder friendly logical groups without messing up the Linked Data interpretation with e.g. blank nodes. I encountered the need for this feature at our company where our problem is that the amount of attributes a single JSON-LD node can have can potentially be quite many, say, tens or hundreds of attributes.
As far as I know, this can not be currently done with JSON-LD without 1) ending up with blank nodes or 2) the need to create a deeper JSON structure by using a separate index term (using "@container":"@index") which then contains the data underneath.
In addition, if a single key could be defined as a index term, this would make it more flexible to attach the JSON-LD Linked Data interpretation to even a wider amount of existing JSON data, without having to change the structure of such data (and without ending up with e.g. lots of blank nodes).
DEFINING AN INDIVIDUAL INDEX KEY IN @context
The "@context" definition could be done e.g. using the existing reserved keyword "@index" in the following way:
"indexkey":"@index"
which should be interpreted in the following way: 1) the "indexkey" is an index key and should be skipped when traversing the JSON tree while doing the JSON-LD to RDF interpretation, 2) any data directly under the "indexkey" should be interpreted as data directly attached to the node of the indexkey (same RDF subject).
EXAMPLE
To give a full example, in the following a single key "labels" is defined as an index index key to help grouping the data into coder friendly logical groups without messing up the Linked Data interpretation):
{
"@context": {
"labels":"@index",
"main_label":"https://example.org/my-schema#main_label",
"other_label":"https://example.org/my-schema#other_label",
"homepage":{ "@id":"https://example.org/my-schema#homepage", "@type":"@id"}
},
"@id":"https://example.org/myresource",
"homepage": "https://example.org",
"labels": {
"main_label": "This is the main label for my resource",
"other_label": "This is the other label"
}
}
This example JSON-LD should generate the following RDF triplets:
<http://example.org/myresource> <http://example.org/my-schema#homepage> <http://example.org>.
<http://example.org/myresource> <http://example.org/my-schema#main_label> "This is the main label for my resource".
<http://example.org/myresource> <http://example.org/my-schema#other_label> "This is the other label".
This has already been discussed several times usingvarious terms.. the most recent request has come from David Janes on the mailing list. The basic idea is to support JSON values/subtrees that aren't mapped to an IRI in the context. They should survive algorithmic transformations (basically without being touched at all).
_This was raised by Fabian Steeg:_
The JSON-LD API document states: "Expansion has two important goals: removing any contextual information from the document, and ensuring all values are represented in a regular form."
Is there a way to achieve only the second goal, the regular form, but with compact terms? Using compaction with compactArrays=false is pretty close, but there is still at least one thing that is irregular and causing issues for me.
Given this input:
{ "https://example.com/foo": "foo-value", "https://example.com/bar": { "@value": "bar-value", "@language": "en" }, "@context": { "foo": "https://example.com/foo", "bar": "https://example.com/bar" } }
I get this from compaction with compactArrays=false:
{ "@graph": [{ "foo": ["foo-value"], <-- foo: array of strings "bar": [{ <-- bar: array of objects "@language": "en", "@value": "bar-value" }] }], "@context": { "foo": "https://example.com/foo", "bar": "https://example.com/bar" } }
But I'd like to get this (which is what expansion does to the values):
{ "@graph": [{ "foo": [{ <-- both foo and bar: "@value" : "foo-value" array of objects }], "bar": [{ "@language": "en", "@value": "bar-value" }] }], "@context": { "foo": "https://example.com/foo", "bar": "https://example.com/bar" } }
So I guess I'm looking for something like a compactValues=false option.
Is there some way to get this output?
We are encountering an issue when converting RDF Datasets to JSON-LD.
The problem is with blank nodes that are shared between graphs and lists.
In TriG (yes, this is a synthetic reduced test case that captures a
smaller example that might appear for real):
# Bnode references across graph and lists
PREFIX : <http://www.example.com/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
:G {
# Written in short form it would be:
# :z :q ("cell-A" "cell-B")
# but we want to share the tail ("cell-B")
:z :q _:z0 .
_:z0 rdf:first "cell-A" .
_:z0 rdf:rest _:z1 .
_:z1 rdf:first "cell-B" .
_:z1 rdf:rest rdf:nil .
}
:G1 {
# This references the tail ("cell-B")
:x :p _:z1 .
}
The triple in :G1 references into the list in :G.
But as we understand the conversion algorithm, section 4 only considers
each graph in turn and so does not see the cross graph sharing.
Is this a correct reading of the spec text?
Part 4 of the conversion algorithm has
"For each name and graph object in graph map: "
so 4.3.3.* walks back up the list in one graph only.
(Conversion generated by jsonld-java : it does not matter if compaction
is applied or not):
{
"@graph" : [ {
"@graph" : [ {
"@id" : ":z",
":q" : {
"@list" : [ "cell-A", "cell-B" ]
}
} ],
"@id" : ":G"
}, {
"@graph" : [ {
"@id" : ":x",
":p" : {
"@id" : "_:b1"
}
} ],
"@id" : ":G1"
} ],
"@context" : {
"@base" : "https://www.example.com/",
"" : "https://www.example.com/",
"rdf" : "https://www.w3.org/1999/02/22-rdf-syntax-ns#"
}
}
There is no _:b1 in :G to refer to because the algorith generated @list
and its implicit bNodes don't have labels.
This is a different dataset with no shared bNode.
If it is all the same graph (s/:G1/:G/), the RDF dataset structure is
correctly serialized.
Andy
See: digitalbazaar/jsonld.js#72
It would be helpful to have the ability to use @language within an object as a shorthand for "@context": {"@language": "..."} ... for instance... make:
{
"@language": "en",
"displayName": "foo"
}
equivalent to:
{
"@context": {"@language": "en"},
"displayName": "foo"
}
In the spirit of "Labeling Everything" (https://patterns.dataincubator.org/book/label-everything.html) ... it would be worthwhile, IMO, for JSON-LD to provide a basic @Label keyword for use both in @context and nodes. It's largely syntactic sugar but would be useful.
For example:
{
"@context": {
"@label": "An Example Context",
"displayName": "@label",
},
"displayName": "A Simple Label"
}
Which would expand to:
_:c14n0 <http://www.w3.org/2000/01/rdf-schema#label> "A Simple Label" .
Problem description §
Many JSON specs existed before JSON-LD. A couple of these specs may not be compatible with JSON-LD as they contain multidimensional containers, such as GeoJSON.
Example of a multidimensional array:
[ [3.1,51.06,30],
[3.1,51.06,20] ]
This issue is a result from the discussion on the GeoJSON-LD repository: geojson/geojson-ld#32. If this issue will not get resolved, the GeoJSON-LD community would suggest creating custom JSON-LD parsers for JSON-LD dialects. This situation would be far from desirable.
Suggested solution §
Introduce a new @values
keyword, which can be used to describe the values of a @set
or a @list
container in more detail.
When an array is given in the @values
, then the precise amount of objects within this array corresponds with the array in the graph in this order.
When an object is given in the @values
, each value of the array in the graph is mapped according to this template.
Example §
{
"@context": {
"coordinates": {
"@id": "geojson:coordinates",
"@container" : "@list",
"@values" : {
"@type" : "geojson:Coordinate",
"@container" : "@set",
"@values" : [
{"@type" : "xsd:double", "@id":"geo:longitude"},
{"@type" : "xsd:double", "@id":"geo:latitude"}
]
}
}
},
"@graph" : [{
"@id" : "ex:LineString1",
"coordinates" : [
[
3.1057405471801753,
51.064216229943476
],
[
3.1056976318359375,
51.063434090307574
]
]
}]
}
Would transform to (and vice versa):
ex:LineString1 geojson:coordinates _:b0 .
_:b0 rdf:first _:b1 .
_:b1 a geojson:Coordinate ;
geo:longitude "3.105740547180175E0"^^xsd:double ;
geo:latitude "5.106421622994348E1"^^xsd:double .
_:b0 rdf:rest _:b2 .
_:b2 rdf:first a geojson:Coordinate ;
geo:longitude "3.1056976318359375"^^xsd:double ;
geo:latitude "51.063434090307574"^^xsd:double .
_:b2 rdf:rest rdf:nil .
I want the following:
{
"@context": {
"type": "@type",
"profile": "@type"
},
"type": "cov:Coverage",
"profile": "cov:GridCoverage"
}
However this is not allowed. The playground says "Invalid JSON-LD syntax; colliding keywords detected".
However, this one works:
{
"@context": {
"type": {"@id": "rdf:type", "@type": "@id" },
"profile": {"@id": "rdf:type", "@type": "@id" }
},
"type": "cov:Coverage",
"profile": "cov:GridCoverage"
}
I understand that this restriction probably makes sense for other keywords, but could it do any harm for @type
?
There have been some discussions on what it would take to be able to do a streaming parse of JSON-LD into Quads, and similarly to generate compliant JSON-LD from a stream of quads. Describing these as some kind of a profile would be useful for implementations that expect to work in a streaming environment, when it's not feasible to work on an entire document basis.
As currently stated, the JSON-LD to RDF algorithm requires expanding the document and creating a node map. A profile of JSON-LD which used a flattened array of node objects, where each node object could be independently expanded and no flattening is required could facilitate deserializing an arbitrarily long JSON-LD source to Quads. (Some simplifying restrictions on shared lists may be necessary). Outer document is an object, containing @context
and @graph
only; obviously, this only will work for systems that can access key/values in order, and for systems that ensure that @context
comes lexically before @graph
in the output. Obviously, only implementations that can read and write JSON objects with key ordering intact will be able to take advantage of such streaming capability.
Fo serializing RDF to JSON-LD, expectations on the grouping of quads with the same graph name and subject are necessary to reduce serialization cost, and marshaling components of RDF Lists is likely not feasible. Even if graph name/subject grouping is not maintained in the input, the resulting output will still represent a valid JSON-LD document, although it may require flattening for further processing. (Many triple stores will, in fact, generate statements/quads properly grouped, so this is likely not an issue in real world applications).
Hi there,
I was looking for a way to access properties in a JSON-LD document based on triples (to patch the document). This would mean having a view which creates a dictionary for a given document. The term Normalisation is already used, but this approach would be close to the way https://github.com/paularmstrong/normalizr. D3 uses https://github.com/d3/d3-hierarchy/blob/master/README.md#stratify in a slightly different way but with the same general intent.
The goal would be to be able to address document values with this syntax stratified_doc[triple.subject][triple.predicate]
or even better stratified[triple.graph][triple.subject][triple.predicate]
.
This could also be a @stratified
parameter for expansion
.
Example §
For a document:
{
"@context": {
"dc": "https://purl.org/dc/elements/1.1/",
"ex": "https://example.org/vocab#",
"xsd": "https://www.w3.org/2001/XMLSchema#",
"ex:contains": {
"@type": "@id"
}
},
"@id": "https://example.org/graph/0",
"dc:creator": "Jane Doe",
"@graph": [
{
"@id": "https://example.org/library",
"@type": "ex:Library",
"ex:contains": "https://example.org/library/the-republic"
}
]
}
Such a stratified
would therefore look like:
{
"https://example.org/graph/0": {
"https://example.org/library": {
"@type": "https://example.org/vocab#Library",
"https://example.org/vocab#contains": {
"@id": "https://example.org/library/the-republic"
}
},
"https://example.org/library/the-republic": {}
},
"@graph": {
"https://example.org/graph/0": {
"https://purl.org/dc/elements/1.1/creator": "Jane Doe"
}
}
}
This would therefore allow to do the following:
// Access a triple from the default graph
var creator = stratified['@graph']['https://example.org/graph/0']['https://purl.org/dc/elements/1.1/creator'];
// "Jane Doe"
// Access a triple in a named graph
var type = stratified['https://example.org/graph/0']['https://example.org/library']['@type'];
// "https://example.org/vocab#Library"
// Before submitting a document, mutate a property
stratified['https://example.org/graph/0']['https://example.org/library/the-republic']['@type'] = 'https://example.org/vocab#Book';
// Or using an immutable spread syntax approach
var new_doc = {
...stratified,
'https://example.org/graph/0': {
...stratified['https://example.org/graph/0'],
'https://example.org/library/the-republic' : {
...stratified['https://example.org/graph/0']['https://example.org/library/the-republic'],
'@type': 'https://example.org/vocab#Book'
}
}
}
Issue: The compaction algorithm prefers the most compact format, which for resources without relationships is a string containing the URI. This causes problems in systems that cannot handle arrays of mixed data types (for example ElasticSearch) when there are also resources that have relationships, resulting in both objects and strings in the same array.
For example:
"seeAlso": [
"https://example.org/reference1",
{"id": "https://example.org/reference2", "format": "text/html"}
]
would raise an error in Elastic.
Proposed solution: Provide a flag to the compaction algorithm to signal that the resulting JSON should always create objects for resources, even if there is only the URI available. This would instead render the example above as an array of objects:
"seeAlso": [
{"id": "https://example.org/reference1"},
{"id": "https://example.org/reference2", "format": "text/html"}
]
The purpose of the @container:@set
functionality (AFAIU) is to ensure that the output is consistent in shape. Thus if there can ever be multiple values, the structure is always an array.
There are two situations in which this functionality could be desirable but is currently not possible:
@type
As it's a keyword, we can only alias it (e.g. astype
) but not define it to have@container:@set
functionality. Meaning that there's a gotcha waiting to happen for ontologies that require or use multiple classes for a single resource instance. See playground@context
Less useful, but@context
will also compact to a single string/object when there might be multiple contexts. See playground
@context
modifying itself seems particularly strange, but the inconsistency for @type
seems solvable if the restrictions in its definition were loosened?
This is related to #235: When I have the following document:
{
"@context": {
"@vocab" : "https://vocab.getty.edu/",
"a" : "https://vocab.getty.edu/aaaaaaaaaat/"
},
"@id" : "https://vocab.getty.edu/aaaaaaaaaat/5001065997",
"@type": "https://vocab.getty.edu/aaaaaaaaaat/datatype"
}
By point 3
of the spec, because https://vocab.getty.edu/aaaaaaaaaat/5001065997
contains the value of @vocab
, it gets compacted as aaaaaaaaaat/5001065997
without even looking at the prefixes. I think this is not reasonable, in this case a:5001065997
would look much nicer IMO.
We have web application that needs to be able to modify RDF lists from a triple store and propagate the changes back. To do this, we are utilizing jsonld-java to serialize the RDF into JSON-LD, modifying it in the web app, and then sending it back to be deserialized and stored in the triple store. Originally, we were using blank nodes like the ones shown in Turtle below.
<http://example.com> <https://example.com/property> _:a .
_:a a <http://www.w3.org/1999/02/22-rdf-syntax-ns#List> ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a" ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b .
_:b a <http://www.w3.org/1999/02/22-rdf-syntax-ns#List> ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b" ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:c .
_:c a <http://www.w3.org/1999/02/22-rdf-syntax-ns#List> ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c" ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <https://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
However, we discovered that blank node lists are collapsed during serialization thus losing all the blank node IDs.
[ {
"@id" : "https://example.com",
"https://example.com/property" : [ {
"@list" : [ {
"@value" : "a"
}, {
"@value" : "b"
}, {
"@value" : "c"
} ]
} ]
} ]
With blank node IDs removed, we are no longer able to reference the existing RDF in the triple store to perform updates when the lists are modified in the web-app without much more complex logic. To avoid this, we skolemized the blank node IDs into IRIs.
<http://example.com> <http://example.com/property> <urn:a> .
<urn:a> a <http://www.w3.org/1999/02/22-rdf-syntax-ns#List> ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a" ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <urn:b> .
<urn:b> a <http://www.w3.org/1999/02/22-rdf-syntax-ns#List> ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b" ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <urn:c> .
<urn:c> a <http://www.w3.org/1999/02/22-rdf-syntax-ns#List> ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c" ;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
However, when serializing the skolemized triples, the IRI for the last element in the RDF list is hidden, in this case urn:c
. This leads to the same problem we were having when using blank node IDs.
[ {
"@id" : "https://example.com",
"https://example.com/property" : [ {
"@id" : "urn:a"
} ]
}, {
"@id" : "urn:a",
"@type" : [ "https://www.w3.org/1999/02/22-rdf-syntax-ns#List" ],
"https://www.w3.org/1999/02/22-rdf-syntax-ns#first" : [ {
"@value" : "a"
} ],
"https://www.w3.org/1999/02/22-rdf-syntax-ns#rest" : [ {
"@id" : "urn:b"
} ]
}, {
"@id" : "urn:b",
"@type" : [ "https://www.w3.org/1999/02/22-rdf-syntax-ns#List" ],
"https://www.w3.org/1999/02/22-rdf-syntax-ns#first" : [ {
"@value" : "b"
} ],
"https://www.w3.org/1999/02/22-rdf-syntax-ns#rest" : [ {
"@list" : [ {
"@value" : "c"
} ]
} ]
} ]
Issue #277 seems to be the point where the implementation changed from serializing lists in the manner we expect to this new compact way. Is there any way we can get around this so that the last blank node of a list is not collapsed?
Currently it appears that properties are sorted into alphabetical order after any JSON-LD operation (compaction, framing).
In the context of framing, this is sometimes a nice feature, since it means that after framing multiple input JSON files, the JSON data is at least in a consistent order.
I understand that ordering is semantically meaningless, but as framing exists to turn the graph (which could correspond to multiple different trees) into a predictable JSON tree as a convenience for developers, it seems natural to me that if an explicit ordering is given in the frame, that the algorithm would respect that order rather than alphabetize. For example, if my data is:
{
"@context": "https://schema.org/",
"@id": "document",
"b": "text",
"a": "more text"
}
Under the frame:
{
"@context": "https://schema.org/",
"@id": "document",
"b": {},
"a": {}
}
the returned document reverses the order of b
and a
(to be alphabetical), and not the order given in the frame. Framing is a really elegant way to specify the nesting order, but it would be nice for framing to also be able to dictate the ordering, so that the output data file really follows the exact structure of the given frame.
Related issue: there is no way to indicate that referenced nodes should occur before they are references (excluding circular references), which can be useful in streaming files. Having control of the node order via the frame would also give a mechanism to address that.
Hope this makes sense and apologies if I'm missing something fundamental here that makes alphabetizing the node order the only logical thing to do; or if I've misunderstood the expected behavior.
Comments at TPAC suggested that as our work is a breaking change (causing 1.0 processors that are not 1.1 compatible to intentionally break when they see "@version": 1.1
), semantic versioning would suggest that we use a major release number, rather than a minor number.
This could impact a potential WG, which may want to make further changes, and then be in the place of using either 2.1
or 3.0
, which is odd given that the previous recommendation is 1.0
.
In some situations it is important/necessary to include the base direction of a text, alongside its language; see the “Requirements for Language and Direction Metadata in Data Formats” for further details. In practice, in a vanilla JSON, it would require something like:
"title": [ { "value": "Moby Dick", "lang": "en" },
{ "value": "موبي ديك", "lang": "ar" "dir": "rtl"}
]
(the example comes from that document).
At this moment, I believe the only way you can reasonably express that in JSON-LD is via cheating a bit:
"title": [ { "@value": "Moby Dick", "@language": "en" },
{ "@value": "موبي ديك", "@language": "ar" "dir": "rtl"}
]
and making sure that the dir
term is not defined in the relevant @context
so that, when generating the RDF output, that term is simply ignored. But that also means that there is no round-tripping, that term will disappear after expansion.
The difficulty lies in the RDF layer, in fact; RDF does not have any means (alas!) to express text direction. On the other hand, this missing feature is a general I18N problem whenever JSON-LD is used (there were issues when developing the Web Annotation Model, these issues are popping up in the Web Publication work, etc.).
Here is what I would propose as a non-complete solution
- Let us introduce a
@dir
term, alongside@language
. This means this term can be used in place ofdir
above, ie, it is a bona-fide part of a string representation, and would therefore be kept in the compaction/expansion steps, can also be used for framing. - In JSON-LD 1.1,
@dir
is ignored when transforming into RDF. I.e., only the language tag would be used. - We may initiate some work in the RDF community to solve this issue. There may be several ways, each of them require the RDF community to chime in
3.1. Define a mechanism of "parametrized" standard datatypes that represent a (language,direction) pair. One would then get something like[] ex:title "موبي ديك"^^rdf:internationalText(ar,rtl) ;
3.2. Go for a "generalized" RDF where strings can also appear as subjects (that has been a matter of dispute for a long time...). That would give the possibility to add such attribute to texts like directions
3.3. Some other mechanisms that I cannot think about - In a future JSON-LD 1.* the
@dir
value can be properly mapped onto an RDF representing the right choices (if such choices are worked out)
Cc: @BigBlueHat @r12a
Per a suggestion by @danbri, we may want to add a container type, similar to @list
for encoding schema:ItemList
serializations, when the values are schema:ListItem
and order is set through schema:position
. ItemList
can be used with text values as well, but this is already reasonably supported natively.
Markup might look like the following:
{
"@context": {
"@vocab": "https://schema.org/",
"itemListElement": {"@container": "@listItem"}
},
"@type": "ItemList",
"@url": "https://en.wikipedia.org/wiki/Billboard_200",
"name": "Top music artists",
"description": "The artists with the most cumulative weeks at number one according to Billboard 200",
"itemListElement": [
{"@type": "MusicGroup", "name": "Beatles"},
{"@type": "MusicGroup", "name": "Elvis Presley"},
{"@type": "MusicGroup", "name": "Michael Jackson"},
{"@type": "MusicGroup", "name": "Garth Brooks" }
]
This would expand to the following:
[
{
"@id": "https://en.wikipedia.org/wiki/Billboard_200",
"@type": ["https://schema.org/ItemList"],
"https://schema.org/description": [{
"@value": "The artists with the most cumulative weeks at number one according to Billboard 200"
}],
"https://schema.org/itemListElement": [{
"@type": ["https://schema.org/ListItem"],
"https://schema.org/item": [{
"@type": ["https://schema.org/MusicGroup"],
"https://schema.org/name": [{"@value": "Beatles"}]
}],
"https://schema.org/position": [{"@value": 1}]
}, {
"@type": ["https://schema.org/ListItem"],
"https://schema.org/item": [{
"@type": ["https://schema.org/MusicGroup"],
"https://schema.org/name": [{"@value": "Elvis Presley"}]
}],
"https://schema.org/position": [{"@value": 2}]
}, {
"@type": ["https://schema.org/ListItem"],
"https://schema.org/item": [{
"@type": ["https://schema.org/MusicGroup"],
"https://schema.org/name": [{"@value": "Michael Jackson"}]
}],
"https://schema.org/position": [{"@value": 3}]
}, {
"@type": ["https://schema.org/ListItem"],
"https://schema.org/item": [{
"@type": ["https://schema.org/MusicGroup"],
"https://schema.org/name": [{"@value": "Garth Brooks"}]
}],
"https://schema.org/position": [{"@value": 3}]
}
],
"https://schema.org/name": [{"@value": "Top music artists"}]
}]
Otherwise, it works like @list
.
When compacting, the processor will re-order items based on position
, and ignore any nextItem
or previousItem
entries.
Expansion shows 1-base position, but could be 0-base as well. Note that specific position
values are lost when compacting, and duplicate values may lead to undefined relative ordering.
During the last meeting it was resolved to have one playground for 1.0 and 1.1 processing. Some notes on that related to jsonld.js:
- 0.4.x passes older 1.0 test suites
- 0.4.x does not pass more recent tests added for 1.0
- 0.5.x does pass all current 1.0 tests
- 0.5.x also includes a bit of 1.1 functionality, in particular some
@graph
handling and a bit of@version
handling - 0.6.x should end up passing all 1.0 and current 1.1 draft tests
- there may be technical issues using multiple jsonld.js versions on one page
From an ease of site development viewpoint, I think we should just put the most recent jsonld.js on the playground and add a UI control to pick the processingMode
API option. Due to practicalities of jsonld.js not having a full correct 1.0 only lib, it seems not worth the effort to try and deal with this any other way. There are edge cases where a 1.1 lib in 1.0 mode will produce different results than a 1.0 lib. My guess is that in practice this really doesn't matter. Or in any case, is not worth handling on the playground.
E. Acknowledgements §
This section is non-normative.
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, Lin Clark, 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 his 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.
F. References §
F.1 Normative references §
- [BCP47]
- Tags for Identifying Languages. A. Phillips; M. Davis. IETF. September 2009. IETF Best Current Practice. URL: https://tools.ietf.org/html/bcp47
- [IEEE-754-2008]
- IEEE 754-2008 Standard for Floating-Point Arithmetic. Institute of Electrical and Electronics Engineers. 2008. URL: https://standards.ieee.org/findstds/standard/754-2008.html
- [JSON-LD]
- JSON-LD 1.0. Manu Sporny; Gregg Kellogg; Markus Lanthaler. W3C. 16 January 2014. W3C Recommendation. URL: https://www.w3.org/TR/json-ld/
- [JSON-LD11CG]
- JSON-LD 1.1. Gregg Kellogg. W3C. CG Final. URL: https://www.w3.org/2018/jsonld-cg-reports/json-ld/
- [RDF-CONCEPTS]
- Resource Description Framework (RDF): Concepts and Abstract Syntax. Graham Klyne; Jeremy Carroll. W3C. 10 February 2004. W3C Recommendation. URL: https://www.w3.org/TR/rdf-concepts/
- [RDF11-MT]
- RDF 1.1 Semantics. Patrick Hayes; Peter Patel-Schneider. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/rdf11-mt/
- [RFC2119]
- Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
- [RFC3986]
- Uniform Resource Identifier (URI): Generic Syntax. T. Berners-Lee; R. Fielding; L. Masinter. IETF. January 2005. Internet Standard. URL: https://tools.ietf.org/html/rfc3986
- [RFC3987]
- Internationalized Resource Identifiers (IRIs). M. Duerst; M. Suignard. IETF. January 2005. Proposed Standard. URL: https://tools.ietf.org/html/rfc3987
- [RFC5988]
- Web Linking. M. Nottingham. IETF. October 2010. Proposed Standard. URL: https://tools.ietf.org/html/rfc5988
- [RFC7159]
- The JavaScript Object Notation (JSON) Data Interchange Format. T. Bray, Ed.. IETF. March 2014. Proposed Standard. URL: https://tools.ietf.org/html/rfc7159
- [XMLSCHEMA11-2]
- W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes. David Peterson; Sandy Gao; Ashok Malhotra; Michael Sperberg-McQueen; Henry Thompson; Paul V. Biron et al. W3C. 5 April 2012. W3C Recommendation. URL: https://www.w3.org/TR/xmlschema11-2/
F.2 Informative references §
- [ECMASCRIPT-6.0]
- ECMA-262 6th Edition, The ECMAScript 2015 Language Specification. Allen Wirfs-Brock. Ecma International. June 2015. Standard. URL: https://www.ecma-international.org/ecma-262/6.0/index.html
- [JSON-LD-API]
- JSON-LD 1.0 Processing Algorithms and API. Markus Lanthaler; Gregg Kellogg; Manu Sporny. W3C. 16 January 2014. W3C Recommendation. URL: https://www.w3.org/TR/json-ld-api/
- [JSON-LD-TESTS]
- JSON-LD 1.1 Test Suite. Gregg Kellogg. Linking Data in JSON Community Group. URL: https://json-ld.org/test-suite/
- [JSON-LD11CG-API]
- JSON-LD 1.1 Processing Algorithms and API. Gregg Kellogg. W3C. CG Final. URL: https://www.w3.org/2018/jsonld-cg-reports/json-ld-api/
- [JSON-LD11CG-FRAMING]
- JSON-LD 1.1 Framing. Gregg Kellogg. W3C. CG Final. URL: https://www.w3.org/2018/jsonld-cg-reports/json-ld-framing/
- [promises-guide]
- Writing Promise-Using Specifications. Domenic Denicola. W3C. 16 February 2016. Finding of the W3C TAG. URL: https://www.w3.org/2001/tag/doc/promises-guide
- [RDF-SCHEMA]
- RDF Schema 1.1. Dan Brickley; Ramanathan Guha. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/rdf-schema/
- [RDF11-CONCEPTS]
- RDF 1.1 Concepts and Abstract Syntax. Richard Cyganiak; David Wood; Markus Lanthaler. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/rdf11-concepts/
- [RFC6839]
- Additional Media Type Structured Syntax Suffixes. T. Hansen; A. Melnikov. IETF. January 2013. Informational. URL: https://tools.ietf.org/html/rfc6839
- [TURTLE]
- RDF 1.1 Turtle. Eric Prud'hommeaux; Gavin Carothers. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/turtle/
- [WEBIDL]
- Web IDL. Cameron McCormack; Boris Zbarsky; Tobie Langel. W3C. 15 December 2016. W3C Editor's Draft. URL: https://heycam.github.io/webidl/