W3C

JSON-LD Algorithms 1.0

Algorithms for Processing JSON-LD Documents

W3C Editor's Draft 25 December 2012

This version:
http://dvcs.w3.org/hg/json-ld/raw-file/default/spec/ED/json-ld-api/20121225/index.html
Latest published version:
http://www.w3.org/TR/json-ld-api/
Latest editor's draft:
http://dvcs.w3.org/hg/json-ld/raw-file/default/spec/latest/json-ld-api/index.html
Editors:
Manu Sporny, Digital Bazaar
Gregg Kellogg, Kellogg Associates
Markus Lanthaler, Graz University of Technology
Authors:
Dave Longley, Digital Bazaar
Manu Sporny, Digital Bazaar
Gregg Kellogg, Kellogg Associates
Markus Lanthaler, Graz University of Technology

This document is also available in this non-normative format: diff to previous version


Abstract

JSON [RFC4627] has proven to be a highly useful object serialization and messaging format. JSON-LD [JSON-LD] harmonizes the representation of Linked Data in JSON by outlining a common JSON representation format for expressing directed graphs; mixing both Linked Data and non-Linked Data in a single document. This document outlines an Application Programming Interface and a set of algorithms for programmatically transforming JSON-LD documents in order to make them easier to work with in programming environments like JavaScript, Python, and Ruby.

Status of This Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

This document has been under development for over 18 months in the JSON for Linking Data Community Group. The document has recently been transferred to the RDF Working Group for review, improvement, and publication along the Recommendation track. The specification has undergone significant development, review, and changes during the course of the last 18 months.

There are several independent interoperable implementations of this specification. There is a fairly complete test suite and a live JSON-LD editor that is capable of demonstrating the features described in this document. While development on implementations, the test suite and the live editor will continue, they are believed to be mature enough to be integrated into a non-production system at this point in time with the expectation that they could be used in a production system within the next year.

Issue

It is important for readers to understand that the scope of this document is currently under debate and new features may be added to the specification. Existing features may be modified heavily or removed entirely from the specification upon further review and feedback from the broader community. This is a work in progress and publication as a Working Draft does not require that all Working Group members agree on the content of the document.

There are a number of ways that one may participate in the development of this specification:

This document was published by the RDF Working Group as an Editor's Draft. If you wish to make comments regarding this document, please send them to public-rdf-comments@w3.org (subscribe, archives). All feedback is welcome.

Publication as an Editor's Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

Table of Contents

1. Introduction

This section is non-normative.

This document is a detailed specification for an Application Programming Interface for the JSON-LD Syntax. The document is primarily intended for the following audiences:

To understand the basics in this specification you must first be familiar with JSON, which is detailed in [RFC4627]. You must also understand the JSON-LD Syntax [JSON-LD], which is the base syntax used by all of the algorithms in this document. To understand the API and how it is intended to operate in a programming environment, it is useful to have working knowledge of the JavaScript programming language [ECMA-262] and WebIDL [WEBIDL]. To understand how JSON-LD maps to RDF, it is helpful to be familiar with the basic RDF concepts [RDF-CONCEPTS].

2. Features

This section is non-normative.

The JSON-LD Syntax specification [JSON-LD] outlines a language that may be used to express Linked Data in JSON. Often, it is useful to be able to transform JSON-LD documents so that they may be easily processed in various programming environments.

There are four major types of transformation that are discussed in this document: expansion, compaction, flattening, and RDF conversion.

2.1 Expansion

This section is non-normative.

Software algorithms are easiest to write when the data that they are processing have a regular form. Since information can be represented by JSON-LD in a variety of different ways, transforming all of these methods into a uniform structure allows the developer to simplify their processing code. For example, note that the following input uses only terms and is fairly compact:

Example 1
{
  "@context": {
    "name": "http://xmlns.com/foaf/0.1/name",
    "homepage": {
      "@id": "http://xmlns.com/foaf/0.1/homepage",
      "@type": "@id"
    }
  },
  "@id": "http://me.markus-lanthaler.com/",
  "name": "Markus Lanthaler",
  "homepage": "http://www.markus-lanthaler.com/"
}

The next input example uses one IRI to express a property, but leaves the rest of the information untouched.

Example 2
{
  "@context": {
    "homepage": {
      "@id": "http://xmlns.com/foaf/0.1/homepage",
      "@type": "@id"
    }
  },
  "@id": "http://me.markus-lanthaler.com/",
  "http://xmlns.com/foaf/0.1/name": "Markus Lanthaler",
  "homepage": "http://www.markus-lanthaler.com/"
}

While both inputs are valid JSON-LD, writing a program to handle every permutation of possible inputs can be difficult, especially when the incoming context could change as well. To ensure that the data can be given a more uniform structure, JSON-LD introduces the notion of expansion. Expansion performs two important operations. The first is to expand all values that represent IRIs to absolute IRIs. The second is to express all values in expanded form. Running the Expansion algorithm against the examples provided above results in the following output:

Example 3
[
  {
    "@id": "http://me.markus-lanthaler.com/",
    "http://xmlns.com/foaf/0.1/name": [
      { "@value": "Markus Lanthaler" }
    ],
    "http://xmlns.com/foaf/0.1/homepage": [
      { "@id": "http://www.markus-lanthaler.com/" }
    ]
  }
]

Note that in the output above all context definitions have been removed, all terms and prefixes have been expanded to absolute IRIs, and all JSON-LD values are expressed in expanded form. While the output is more difficult for a human to read, it is easier for a software program to process because of its very regular structure.

2.2 Compaction

This section is non-normative.

While expansion expands a given input as much as possible, compaction performs the opposite operation: it expresses a given input as succinctly as possible. In contrast to expansion which is meant to produce something that is easy to process by software programs, compaction is meant to produce something that is easy to read by software developers. Compaction uses a developer-supplied context to compress IRIs to terms or compact IRIs and JSON-LD values expressed in expanded form to simple values such as strings and numbers.

For example, assume the following expanded JSON-LD input document:

Example 4
[
  {
    "@id": "http://me.markus-lanthaler.com/",
    "http://xmlns.com/foaf/0.1/name": [
      { "@value": "Markus Lanthaler" }
    ],
    "http://xmlns.com/foaf/0.1/homepage": [
      { "@id": "http://www.markus-lanthaler.com/" }
    ]
  }
]

Additionally, assume the following developer-supplied JSON-LD context:

Example 5
{
  "@context": {
    "name": "http://xmlns.com/foaf/0.1/name",
    "homepage": {
      "@id": "http://xmlns.com/foaf/0.1/homepage",
      "@type": "@id"
    }
  }
}

Running the Compaction Algorithm given the context supplied above against the JSON-LD input document provided above would result in the following output:

Example 6
{
  "@context": {
    "name": "http://xmlns.com/foaf/0.1/name",
    "homepage": {
      "@id": "http://xmlns.com/foaf/0.1/homepage",
      "@type": "@id"
    }
  },
  "@id": "http://me.markus-lanthaler.com/",
  "name": "Markus Lanthaler",
  "homepage": "http://www.markus-lanthaler.com/"
}

Note that all IRIs have been compacted to terms as specified in the context which consequently has been injected into the output. While compacted output is most useful to humans, it can often also be 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 http://xmlns.com/foaf/0.1/name to name, it could have 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 and ensures that also the shape of the data is deterministic. In expanded documents properties of a single node may still be spread across a number of different JSON objects. By flattening a document, all properties of a node are collected in a single JSON object and all blank nodes are labeled with a blank node identifier. Often this drastically simplifies the code to process JSON-LD data.

For example, assume the following JSON-LD input document:

Example 7
{
  "@context": {
    "name": "http://xmlns.com/foaf/0.1/name",
    "knows": "http://xmlns.com/foaf/0.1/knows"
  },
  "@id": "http://me.markus-lanthaler.com/",
  "name": "Markus Lanthaler",
  "knows": [
    {
      "name": "Manu Sporny",
      "knows": {
        "@id": "http://greggkellogg.net/foaf#me"
      }
    },
    {
      "@id": "http://greggkellogg.net/foaf#me",
      "name": "Gregg Kellogg"
    }
  ]
}

Running the Flattening Algorithm with a context set to null to prevent compaction returns the following document:

Example 8
[
  {
    "@id": "http://me.markus-lanthaler.com/",
    "http://xmlns.com/foaf/0.1/name": [
      { "@value": "Markus Lanthaler" }
    ],
    "http://xmlns.com/foaf/0.1/knows": [
      { "@id": "_:t0" },
      { "@id": "http://greggkellogg.net/foaf#me" }
    ]
  },
  {
    "@id": "_:t0",
    "http://xmlns.com/foaf/0.1/name": [
      { "@value": "Manu Sporny" }
    ],
    "http://xmlns.com/foaf/0.1/knows": [
      { "@id": "http://greggkellogg.net/foaf#me" }
    ]
  },
  {
    "@id": "http://greggkellogg.net/foaf#me",
    "http://xmlns.com/foaf/0.1/name": [
      { "@value": "Gregg Kellogg" }
    ]
  }
]

Note how in the output above all properties of a node are collected in a single JSON object and how the blank node representing "Manu Sporny" has been assigned the blank node identifier _:t0.

To make it easier for humans to read such 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:

Example 9
{
  "@context": {
    "name": "http://xmlns.com/foaf/0.1/name",
    "knows": "http://xmlns.com/foaf/0.1/knows"
  },
  "@graph": [
    {
      "@id": "http://me.markus-lanthaler.com/",
      "name": "Markus Lanthaler",
      "knows": [
        { "@id": "_:t0" },
        { "@id": "http://greggkellogg.net/foaf#me" }
      ]
    },
    {
      "@id": "_:t0",
      "name": "Manu Sporny",
      "knows": {
        "@id": "http://greggkellogg.net/foaf#me"
      }
    },
    {
      "@id": "http://greggkellogg.net/foaf#me",
      "name": "Gregg Kellogg"
    }
  ]
}

Please note that the flattened and compacted result will always explicitly designate the default by the @graph member in the top-level JSON object. Compaction optimizes that member away if its value contains just one item.

2.4 RDF Conversion

This section is non-normative.

JSON-LD can be used to serialize data expressed in RDF as described in [RDF-CONCEPTS]. This ensures that data can be round-tripped from and to any RDF syntax without any loss in the fidelity of the data.

For example, assume the following RDF input serialized in Turtle [TURTLE-TR]:

Example 10
<http://me.markus-lanthaler.com/> <http://xmlns.com/foaf/0.1/name> "Markus Lanthaler" .
<http://me.markus-lanthaler.com/> <http://xmlns.com/foaf/0.1/homepage> <http://www.markus-lanthaler.com/> .

Using the Convert from RDF Algorithm a developer could transform this document into expanded JSON-LD:

Example 11
[
  {
    "@id": "http://me.markus-lanthaler.com/",
    "http://xmlns.com/foaf/0.1/name": [
      {
        "@value": "Markus Lanthaler"
      }
    ],
    "http://xmlns.com/foaf/0.1/homepage": [
      {
        "@id": "http://www.markus-lanthaler.com/"
      }
    ]
  }
]

Note that the output above could easily be compacted using the technique outlined in the previous section. It is also possible to transform the JSON-LD document back to RDF using the Convert to RDF Algorithm.

3. Conformance

All examples and notes as well as sections marked as non-normative in this specification are non-normative. Everything else in this specification is normative.

The keywords must, must not, required, should, should not, recommended, may, and optional in this specification are to be interpreted as described in [RFC2119].

There are two classes of products that can claim conformance to this specification: JSON-LD Implementations and JSON-LD Processors.

A conforming JSON-LD Implementation is a system capable of transforming JSON-LD documents according the algorithms defined in this specification.

A conforming JSON-LD Processor is a conforming JSON-LD Implementation that exposes the application programming interface (API) defined in this specification.

The algorithms in this specification are generally written with more concern for clarity than over efficiency. Thus, JSON-LD Implementations and 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.

This specification does not define how JSON-LD Implementations or Processors handle non-conforming input documents. This implies that JSON-LD Implementations or Processors must not attempt to correct malformed IRIs or language tags; however, they may issue validation warnings.

Note

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. General Terminology

This document uses the following terms as defined in JSON [RFC4627]. Refer to the JSON Grammar section in [RFC4627] for formal definitions.

JSON object
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.
array
An array structure is represented as square brackets surrounding zero or more values (or elements). Elements are separated by commas. In JSON, an array is an ordered sequence 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 specific markup is provided (see ).
string
A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes (if necessary). A character is represented as a single character string.
number
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.
true and false
Values that are used to express one of two possible boolean states.
null
The null value. A key-value pair in the @context where the value, or the @id of the value, is null explicitly decouples a term's association with an IRI. A key-value pair in the body of a JSON-LD document whose value is null has the same meaning as if the key-value pair was not defined. If @value, @list, or @set is set to null in expanded form, then the entire JSON object is ignored.

Furthermore, the following terminology is used throughout this document:

keyword
A JSON key that is specific to JSON-LD, specified in the JSON-LD Syntax specification [JSON-LD] in the section titled Syntax Tokens and Keywords.
context
A a set of rules for interpreting a JSON-LD document as specified in The Context of the [JSON-LD] specification.
JSON-LD document
A JSON-LD document is a serialization of a collection of JSON-LD graphs and comprises exactly one default graph and zero or more named graphs.
named graph
A named graph is a pair consisting of an IRI or blank node (the graph name) and a JSON-LD graph.
default graph
The default graph is the only graph in a JSON-LD document which has no graph name.
JSON-LD 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 syntax specification [JSON-LD].
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.
node
Every node is an IRI, a blank node, a JSON-LD value, or a list.
IRI
An IRI (Internationalized Resource Identifier) is a string that conforms to the syntax defined in [RFC3987].
absolute IRI
An absolute IRI is defined in [RFC3987] containing a scheme along with a path and optional query and fragment segments.
relative IRI
A relative IRI is an IRI that is relative some other absolute IRI; in the case of JSON-LD this is the base location of the document.
blank node
A node in a JSON-LD graph that 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 JSON-LD graph.
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 _:.
JSON-LD value
A JSON-LD value is a string, a number, true or false, a typed value, or a language-tagged string.
typed value
A typed value consists of a value, which is a string, and a type, which is an IRI.
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 of [BCP47], and must be normalized to lowercase.
list
A list is an ordered sequence of IRIs, blank nodes, and JSON-LD values.

5. Algorithms

All algorithms described in this section are intended to operate on language-native data structures. That is, the serialization to a text-based JSON document isn't required as input or output to any of these algorithms and language-native data structures must be used where applicable.

5.1 Algorithm Terms

active graph
The name of the currently active graph that the processor should use when processing.
active subject
The currently active subject that the processor should use when processing.
active property
The currently active property that the processor should use when processing. The active property is represented in the original lexical form, which is used for finding type mappings in the active context.
active object
The currently active object that the processor should use when processing.
active context
A context that is used to resolve terms while the processing algorithm is running. The active context is the context contained within the processor state.
local context
A context that is specified within a JSON object, specified via the @context keyword.
processor state
The processor state, which includes the active context, active subject, and active property. The processor state is managed as a stack with elements from the previous processor state copied into a new processor state when entering a new JSON object.
JSON-LD input
The JSON-LD data structure that is provided as input to the algorithm.
JSON-LD output
The JSON-LD data structure that is produced as output by the algorithm.
term
A term is a short word defined in a context that may be expanded to an IRI
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.
node object
A node object represents zero or more properties of a node in the JSON-LD 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-level JSON object in the JSON-LD document containing the @graph keyword.
list object
A list object is a JSON object that has a @list member.
scalar
A scalar is either a JSON string, number, true, or false.
quad
An RDF triple as specified by [RDF-CONCEPTS] augmented with a a fourth component, a graph name.

5.2 Expansion Algorithm

The algorithm takes three input variables: an active context, an active property, and an element to be expanded. To begin, the active context is set to the result of performing, Context Processing on the passed expandContext, or empty if expandContext is null, active property is set to null, and element is set to the JSON-LD input. This algorithm expects the JSON-LD input to be a well-formed JSON-LD document as defined in [JSON-LD].

  1. If element is a scalar, expand it according to the Value Expansion algorithm, passing copies of the active context and active property and return.
  2. If element is null, return.
  3. If element is an array,
    1. initialize an empty array result.
    2. Expand each item by recursively using this algorithm, passing copies of the active context and active property.
    3. If the active property's container mapping is set to @list and the expanded item is an array or a list object trigger a LIST_OF_LISTS_DETECTED error.
    4. If the expanded item is null, drop it.
    5. Otherwise, if the expanded item is an array, merge its entries with result's entries.
    6. Otherwise, append item to result.
    7. Finally, set element to result and return.
  4. Otherwise, element must be an object.
    1. If element has a @context member, update the active context according to the steps outlined in Context Processing and remove the @context member.
    2. Initialize an empty JSON object result and
    3. then process each property and value in element ordered by property as follows:
      1. If the active context contains a property generator for property set expanded property to its IRIs, otherwise set it to the result of expanding property according to the steps outlined in IRI Expansion (passing true for the vocabRelative flag).
      2. If expanded property is null, skip the current property-value pair and continue to the next property-value pair in element.
      3. If expanded property is a keyword, process it as follows:
        1. If expanded property equals @id, set the @id member of result to the result of expanding value according the IRI Expansion algorithm (passing true for the documentRelative flag). If value is not a string trigger an INVALID_ID_VALUE error.
        2. If expanded property equals @type, set the @type member of result to the result of expanding value according the IRI Expansion algorithm (passing true for both the documentRelative and the vocabRelative flag). If value is neither a string nor an array of strings trigger an INVALID_TYPE_VALUE error. Empty arrays are ignored.
        3. If expanded property equals @value, set the @value member of result to value. If value is neither a scalar nor null trigger an INVALID_VALUE_OBJECT_VALUE error.
        4. If expanded property equals @language, set the @language member of result to the lowercased value. If value is not a string, trigger an INVALID_LANGUAGE_VALUE error.
        5. If expanded property equals @annotation, set the @annotation member of result to value. If value is not a string trigger an INVALID_ANNOTATION_VALUE error.
        6. If expanded property equals @set or @list, set the expanded property member of result to the result of expanding value by recursively using this algorithm, passing copies of the active context and active property.
        7. If expanded property equals @graph, set the @graph member of result to the result of expanding value by recursively using this algorithm, passing copies of the active context and @graph as active property.
        8. Continue with the next property-value pair from element.
      4. If expanded property is not an absolute IRI,i.e., it doesn't contain a colon, continue with the next member from element.
      5. Otherwise, if property's container mapping is set to @language
        1. Initialize a new empty array language map values.
        2. Process each key-val pair of value ordered by key as follows:
          1. If val is not an array, transform it to one.
          2. For each item of val, construct a new JSON object consisting of two members: @value set to the currently processed item and @language set to the lowercased key. If val is not a string, trigger a LANGUAGE_MAP_INVALID_VALUE error. Otherwise append the object to language map values.
        3. Set value to language map values.
      6. Otherwise, if property's container mapping is set to @annotation
        1. Initialize a new empty array annotation map values.
        2. Process each key-val pair of value ordered by key as follows:
          1. If val is not an array, transform it to one.
          2. Expand val by recursively using this algorithm, passing copies of the active context and active property.
          3. Add to each item of val a member @annotation set to key if no such member exists yet and append the resulting JSON object to annotation map values.
        3. Set value to annotation map values.
      7. Otherwise, expand value by recursively using this algorithm, passing copies of the active context and property as active property.
      8. If the expanded value equals null, continue with the next property-value pair from element.
      9. If property's container mapping is set to @list and value is either not an JSON object or a JSON object without an @list member, replace value with a JSON object with an @list member whose value is set to value (wrapped in an array if it is not already one).
      10. If expanded property is an array,
        1. label all blank nodes in value with blank node identifiers by using the Label Blank Nodes Algorithm.
        2. Then, for each iri of expanded property merge a copy of value into the iri member of the result JSON object.
      11. Otherwise, merge value into the iri member of the result JSON object.
    4. Set element to result and numProperties to the number of members of result.
    5. If element has an @annotation member, decrease numProperties by 1.
    6. If element has an @value member,
      1. decrease numProperties by 1.
      2. If element has an @language member, decrease numProperties by 1 and check that the value of the @value member is a string. If not, trigger an INVALID_LANGUAGE_TAGGED_STRING error.
      3. Otherwise, if element has an @type member, decrease numProperties by 1 and check that the value of the @type member is a string. If not, trigger an INVALID_TYPED_VALUE error.
      4. If numProperties is greater than 0, trigger an INVALID_VALUE_OBJECT error.
      5. If the value of the @value member equals null, set element to null.
      6. Return.
    7. If element has an @type member whose value is not an array, transform it to an array.
    8. If element has an @list or @set member and numProperties is greater than 1, trigger an INVALID_SET_OR_LIST_OBJECT error.
    9. Otherwise, if element has an @set member, set element to the value of that member.
    10. Otherwise, if element has just an @language member, set element to null.

If, after the algorithm outlined above is run, the resulting element is an JSON object with just a @graph member, element is set to the value of @graph's value. Finally, if element is a JSON object, it is wrapped into an array.

5.3 Context Processing

Processing of JSON-LD data structure is managed recursively. During processing, each rule is applied using information provided by the 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 vocabulary mapping and the default language. Each term definition consists of an IRI mapping and optionally a type mapping from terms to datatypes or language mapping from terms to language codes, and a container mapping. If an IRI mapping maps a term to multiple IRIs it is said to be a property generator.

If a local context is encountered, information from the local context is merged into the active context. A local context is identified within a JSON object having a @context member with a string, array or a JSON object value.

This algorithm specifies how the active context is updated with a local context. The algorithm takes three input variables: an active context, a local context, and an array of already included remote contexts remoteContexts. To begin, remoteContexts is initialized to an empty array.

All calls of the IRI Expansion algorithm pass the value specified in the algorithm along with the active context, the currently being processed local context, and true for the vocabRelative flag.

  1. If the local context is not an array, transform it to one.
  2. Process each item context of the local context as follows:
    1. If context equals null, reset the active context and continue with the next item.
    2. If context is a string:
      1. Expand context according to IRI Expansion.
      2. If context is in the remoteContexts array, raise an RECURSIVE_CONTEXT_INCLUSION error. Otherwise, add context to remoteContexts.
      3. Dereference context.
      4. If the resulting document is a JSON document consisting of a top-level JSON object that has a @context member recursively invoke this algorithm passing a copy of active context, the value of the @context member as local context, and a copy of the remoteContexts array. Relative IRIs are expanded using the remote context's IRI. Otherwise raise an INVALID_REMOTE_CONTEXT error.
      5. Continue with the next item from context.
    3. If context is not a JSON object, raise an INVALID_LOCAL_CONTEXT error.
    4. Otherwise, if context is an JSON object, perform the following steps:
      1. If context has a @vocab member: if its value is neither an absolute IRI, i.e., it does not contain a colon (:), nor null, trigger an INVALID_VOCAB_MAPPING error; otherwise set the active context's vocabulary mapping to its value and remove the @vocab member from context.
      2. If context has a @language member: if its value is neither a string nor null, trigger an INVALID_DEFAULT_LANGUAGE error; otherwise set the active context's default language to its value and remove the @language member from context.
      3. For each other key-value pair in context perform the following steps:
        1. Remove the key-value pair from context.
        2. If key is a JSON-LD keyword, continue with the next key-value pair.
        3. If value equals null, replace the term definition for key in the active context with an IRI mapping set to null and continue with the next key-value pair.
        4. If value is a string, expand it according to the IRI Expansion algorithm and replace the term definition for key in the active context with an IRI mapping set to the expanded value. Continue with the next key-value pair.
        5. If value is not a JSON object, trigger an INVALID_TERM_DEFINITION error
        6. If term definition for key exists in the active context, remove it.
        7. Initialize a new, empty term definition definition.
        8. If value has an @id member with a value val:
          1. if val is null, set definition to val.
          2. if val is an array, process each item in the array:
            1. if item is a string expand it according the IRI Expansion algorithm.
            2. otherwise, raise an INVALID_PROPERTY_GENERATOR error.
          3. if val is an array, lexicographically sort val and set definition to val.
          4. Otherwise, if val is a string, expand it according the IRI Expansion algorithm and set definition to val.
        9. If the IRI mapping of definition is set to null or a keyword, store the definition as the term definition for key in the active context and continue with the next key-value pair from context.
        10. Otherwise, set the IRI mapping of definition to the result of expanding key according the IRI Expansion algorithm.
        11. If value has an @type member with a value val and val is not a string or does not expand to an absolute IRI using the IRI Expansion algorithm, raise an INVALID_TYPE_MAPPING error. Otherwise set the IRI mapping of definition to the expanded val.
        12. Otherwise, if value has an @language member with a value val that is a string or null, set the language mapping of definition to the lowercased val. If val is neither a string nor null, raise an INVALID_LANGUAGE_MAPPING error.
        13. f value has an @container member with a value val that equals @list, @set, or @annotation, set the container mapping of definition to val. If val is not one of those values, raise an INVALID_CONTAINER_MAPPING error.

5.4 IRI Expansion

In JSON-LD documents keys and some values are evaluated to produce an IRI. This section defines an algorithm for transforming strings representing an IRI into an absolute IRI. If IRI expansion occurs during context processing, the local context that is being processed is passed to this algorithm. After application of this algorithm, values processed by this algorithm are said to be in expanded IRI form, although this may also include blank node identifiers and JSON-LD keywords.

The algorithm takes two mandatory and four optional input variables: a value to be expanded, an active context, two flags documentRelative and vocabRelative specifying whether value should be interpreted as relative IRI against the document's base IRI or the active context's vocabulary mapping, along with an local context passed when this algorithm is used in Context Processing, and finally an array path which is used to detect cyclic IRI mappings. If not passed, the two flags are set to false and path is initialized to an empty array by default.

The algorithm for generating an IRI is:

  1. If value is null or a JSON-LD keyword, return value as is.
  2. If a local context has been passed
    1. and value is in the path array, raise a CYCLIC_IRI_MAPPING error. Otherwise append value to path.
    2. If local context contains an IRI mapping for value that is not a property generator return the result of recursively calling this algorithm passing the IRI of the IRI mapping as new value, the active context and local context, path, and true for the vocabRelative flag. If the result is a property generator, raise an PROPERTY_GENERATOR_IN_TERM_DEFINITION error.
  3. If an IRI mapping exists for value in the active context that is not a property generator return the value of the IRI mapping.
  4. If value contains a colon (:), perform the following steps:
    1. Split value into a prefix and suffix at the first occurrence of a colon (:).
    2. If suffix begins with // or prefix equals _, return value as is.
    3. If a local context has been passed, expand prefix by recursively invoking this algorithm passing prefix as value, the active context and local context, path, and true for the vocabRelative flag. If the expanded prefix contains a colon (:) generate and return an IRI by prepending the expanded prefix to the (possibly empty) suffix using textual concatenation.
    4. Otherwise, if the active context contains an IRI mapping for prefix that is not a property generator, generate and return an IRI by prepending the IRI mapped to prefix to the (possibly empty) suffix using textual concatenation.
  5. Otherwise, if the vocabRelative flag is set to true and the active context contains a vocabulary mapping, generate and return an IRI by prepending the IRI of the vocabulary mapping to the value using textual concatenation.
  6. Otherwise, if the documentRelative flag is set to true, resolve value against the base IRI as per [RFC3986] and return the resulting IRI. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization (as described in sections 6.2.2 and 6.2.3 of [RFC3986]) 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]
  7. Otherwise return value as is.

If the result of the algorithm above is a blank node identifier, i.e., a string that begins with _:, and no local context has been passed, generated a new blank node identifier before returning the final result.

5.5 Value Expansion

Some values in JSON-LD can be expressed in a compacted 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.

The algorithm for expanding a value takes an active property and active context. It is implemented as follows:

  1. If value is null, return null.
  2. Initialize an empty object result.
  3. If active property is @graph or the active property's type mapping is set to @id, add a key-value pair to result where the key is @id and the value is the result of expanding value according to the IRI Expansion algorithm passing true for the documentRelative flag. Then return result.
  4. Add a key-value pair to result where the key is @value and the value is value.
  5. If the active property has a type mapping, add a key-value pair to result where the key is @type and the value is the IRI associated with the type mapping or a newly generated blank node identifier if the type mapping is set to a blank node identifier.
  6. Otherwise, if value is a string and the active property has a language mapping or an default language is set in the active context, add a key-value pair to result where the key is @language and the value is the language tag associated with the language mapping or the active property or the default language.
  7. Return result.

5.6 Label Blank Nodes Algorithm

The algorithm takes a single input variable: an element to be labeled with blank node identifiers.

  1. If element is an array, recursively apply this algorithm to all its items.
  2. Otherwise, if element is a JSON object with a @list member, recursively apply this algorithm to all items of the @list member's value.
  3. Otherwise, if element is a JSON object
    1. For each key-value pair ordered by key recursively apply this algorithm to value.
    2. If element is a node object without an @id member, create a new @id member and assign it a new blank node identifier according the Generate Blank Node Identifier algorithm.

5.7 Generate Blank Node Identifier

This algorithm is used to generate new blank node identifiers or to relabel existing blank node identifiers with a new one to avoid collision by the introduction of new ones. It needs to keep an identifier map, a counter, and a prefix between its executions to be able to generate new blank node identifiers. The counter is initialized to 0 and prefix is set to _:t by default.

The algorithm takes a single input variable identifier which might be null.

  1. If the identifier is not null and is in the identifier map, return the mapped identifier.
  2. Otherwise, generate a new blankNodeIdentifier by concatenating prefix and counter.
  3. Increment counter by 1.
  4. If identifier is not null, create a new entry in identifier map set to blankNodeIdentifer.
  5. Return blankNodeIdentifier.

5.8 Compaction Algorithm

The algorithm takes four input variables: an active context, an inverse context, an active property, and an element to be compacted. To begin, the active context is set to the result of performing Context Processing on the passed context, inverse context is set to the result of creating an inverse context from the active context, active property is set to null, and element is set to the result of performing the Expansion Algorithm on the JSON-LD input.

  1. If element is an array,
    1. Initialize a new empty array result.
    2. Process each item in element recursively using this algorithm, passing a copy of the active context, inverse context, and the active property. Add each compacted item to result unless it is null.
    3. If result has a single item and the compactArrays option is set to true, return that item; otherwise return result.
  2. If element is not a JSON object it is already in compact form, return it as is.
  3. If element has an @value or @id member, replace element with the result of the Value Compaction algorithm. If the updated element is a scalar, return it as it cannot be further compacted.
  4. Initialize a new empty JSON object result.
  5. Process each property and value in element ordered by property as follows:
    1. If property is a JSON-LD keyword
      1. and property equals @id, compact value according the rules of the IRI Compaction algorithm.
      2. Otherwise, if property equals @type, compact value (or each item of value if it is an array) according the rules of the IRI Compaction algorithm with the vocabRelative flag set to true. If value is an array consisting of just one item, replace value with that item.
      3. Otherwise, if property equals @graph, compact value by recursively invoking this algorithm, passing a copy of the active context, inverse context, and property as active property ensuring that the result is an array.
      4. Set active property to the result of performing IRI Compaction on property.
      5. Set the active property member of result to value.
      6. Continue with the next property-value pair from element.
    2. If value is an empty array,
      1. set active property to the result of performing IRI Compaction on property with the vocabRelative flag set to true.
      2. If active property is a JSON object, i.e., it is a property generator, set active property to the result of performing the Find and Remove Property Generator Duplicates algorithm passing element, property, null for value, the active context, and active property.
      3. Ensure that result has an active property member; if not create it and set its value to an empty array.
      4. Continue with the next property-value pair from element.
    3. Otherwise perform the following steps for each item of value:
      1. Set active property to the result of performing IRI Compaction on property with the vocabRelative flag set to true.
      2. If active property is a JSON object, i.e., it is a property generator, set active property to the result of performing the Find and Remove Property Generator Duplicates algorithm passing element, property, item, the active context, and active property.
      3. If the active property's container mapping is set to @language or @annotation
        1. Unless result has already an active property member, create one and initialize its value to an empty JSON object. This object is called mapObject.
        2. Set index to the value of the @language or @annotation member of item (depending on the value of the active property's container mapping).
        3. First compact item using the Value Compaction algorithm passing index as containerValue, then compact it by recursively invoking this algorithm passing a copy of the active context, inverse context, and the active property.
        4. If no index member exists in the mapObject create one and set its value to item; otherwise append item to the index member (converting it to an array if it is not one already).
        5. Continue with the next property-value pair from element.
      4. If item is a JSON object having a @list member,
        1. compact the value of that member by recursively invoking this algorithm passing a copy of the active context, inverse context, and the active property ensuring that the result is an array.
        2. If the active property's container mapping is set to @list, set the active property member of result to the value of item's @list member. If such an member already exists in result, raise an COMPACTION_TO_LIST_OF_LISTS error; otherwise continue with the next property-value pair from element.
      5. If item is a JSON object, compact it by recursively invoking this algorithm passing a copy of the active context, inverse context, and the active property.
      6. If no active property member exists in result create one and set its value to item; otherwise append item to the active property member (converting it to an array if it is not one already).
      7. If the compactArrays option is set to false or the active property's container mapping is set to @list or @set, convert the value of the active property member of result to an array if it is not one already.

    If, after the algorithm outlined above is run, the resulting element is an array replace it with a new JSON object with a single member whose name is the result of compacting the value @graph with the IRI Compaction algorithm and whose value is element. Finally, add a @context property to element and set it to the initially passed context.

5.9 IRI Compaction Algorithm

This section defines an algorithm for transforming an IRI to a term or compact IRI. If a value is passed it is used to choose the best matching term.

This algorithm takes three mandatory and two optional parameters. The mandatory parameters are the iri to be compacted, an active context, and an inverse context. Optionally it is possible to pass a value and a vocabRelative flag which specifies whether the passed iri should be compacted using the active context's vocabulary mapping. If the vocabRelative flag is not set it defaults to false.

The algorithm for generating a compact IRI is:

  1. Initialize a variable result to null.
  2. If an entry for iri exists in the inverse context, perform the following steps:
    1. If a value has been passed, perform the following steps:
      1. Initialize queryPath, which will be used to query the inverse context, to an empty array
      2. Initialize container to @set, typeOrLanguage, and typeLanguageValue to null.
      3. If value is a JSON object
        1. and it has an @annotation member, set container to @annotation.
        2. If value has an @id member, set typeOrLanguage to @type and typeLanguageValue to @id.
        3. Otherwise, if value has an @value member,
          1. and an @type member, set typeOrLanguage to @type and typeLanguageValue to the value of the @type member of value.
          2. Otherwise, if it has an @language member, set typeOrLanguage to @language and typeLanguageValue to the value of the @language member of value. If value has no @annotation member, set container to @language
          3. Otherwise, if the value of value's @value member is is a string, set typeOrLanguage to @language and typeLanguageValue to @null.
        4. Otherwise, if value has an @list member,
          1. If the @list member has at least one item, update container, typeOrLanguage, and typeLanguageValue by recursively running the steps 2.1.2 to 2.1.3.4 (which will never be true since list of lists are not allowed) of this algorithm passing the first item of value's @list member as new value.
          2. If value has no @annotation member, set container to @list.
          3. Starting from the second item of value's @list member, recursively run the steps 2.1.2 to 2.1.3.4 (which will never be true since list of lists are not allowed) of this algorithm passing the item as new value. If the resulting typeOrLanguage or typeLanguageValue differ from the current value of typeOrLanguage or typeLanguageValue, set both to null and stop processing the @list items.
      4. If the container equals @list, set the first item of queryPath to an array consisting of the two elements @list and null; otherwise set it to an array consisting of three elements where the first element is the value of container and the other two elements are @set and @null.
      5. If typeOrLanguage is null, set the second and the the third item of queryPath to an array consisting of a single element @null.
      6. Otherwise, set the second item of queryPath to an array consisting of two elements where the first element is the value of typeOrLanguage and the second element is @null. Set the third item of queryPath to an array whose first element typeLanguageValue and whose second element is @null.
      7. Query the inverse context and store the result in result.
      8. If result is a a string or a JSON object with a term member, return result.
    2. Otherwise, if the entry for iri in the inverse context has a term member, return its value.
  3. For each termIri-termDefinition pair in inverse context sorted in reverse order by termIri the (longest termIri comes first), perform the following steps:
    1. If termDefinition does not have a term member, i.e., it is a property generator, continue with the next termIri-termDefinition pair from inverse context.
    2. Otherwise, if iri begins with termIri and is longer than termIri, generate a compact IRI by concatenating the value of the term member of termDefinition with a colon (:) character and the unmatched part of iri.
    3. If the resulting compact IRI has an entry in the active context, continue with the next termIri-termDefinition pair from inverse context as the compact IRI cannot be used.
    4. Otherwise, if result is null, return the compact IRI; if it is not null, set the term member of result to the compact IRI and return result.
  4. If the vocabRelative flag is set to true, the active context has a vocabulary mapping, and iri begins with the IRI of the vocabulary mapping but is longer
    1. Set vocabIri to the unmatched part of iri.
    2. If there does not exist an entry for vocabIri in the active context, return vocabIri if result is null; if it is not null set the term member of result to vocabIri and return result.
  5. If result is null, return iri as is.
  6. Otherwise set the term member of result to iri and return result.

5.10 Inverse Context Creation

An active context as produced by the Context Processing algorithm is very efficient for expanding terms and compact IRIs to IRIs but is of limited use for the opposite operation: IRI compaction. Hence, this algorithm introduces the notion of an inverse context which brings the same efficiency to IRI compaction. An inverse context a tree of JSON objects which allow to efficiently select a term for a IRI-value pair.

The value takes an active context and returns the corresponding inverse context.

  1. Set defaultLanguage to active context'sdefault language if there is one; otherwise set it to @null.
  2. For each term definition in the active context perform the following steps:
    1. If the term's IRI mapping is set to null continue with the next term definition.
    2. Set container to the value of the term's container mapping or @null if no such mapping exists.
    3. If the term is a property generator, set termType to propertyGenerators.
    4. Otherwise set set termType to term and append the term to the term member of the JSON object for the term's IRI in inverse context (append term to inverseContext[iri]['term']).
    5. For each iri mapped to the term, perform the following steps:
      1. If the term has a type mapping to type, append the term to inverseContext[iri][container]['@type'][type][termType], e.g., inverseContext['http://...']['@list']['@type']['http://...']['term'].
      2. Otherwise, if the term has a language mapping store the associated language in language, replacing null with @null. Then append the term to inverseContext[iri][container]['@language'][language][termType], e.g., inverseContext['http://...']['@list']['@language']['de']['term'].
      3. Otherwise append the term to inverseContext[iri][container]['@null']['@null'][termType] as well as inverseContext[iri][container]['@language'][defaultLanguage][termType] to take the default language into consideration for terms without explicit language mapping.
  3. Remove all but the shortest and lexicographically least term in each term member in inverse context. The result is thus a single string value.
  4. Finally, sort the array associated with every propertyGenerators member in inverse context lexicographically (shortest terms come first).

5.11 Inverse Context Query Algorithm

It is possible that multiple terms that differ in their container, type, or language mapping are mapped to the same IRI. The purpose of this algorithm is to query the inverse context to return either the best matching term, or a list of potential property generators along with a term to fall back to if none of the property generators can be applied.

This algorithm takes an inverseContextSubtree, a queryPath as generated by the IRI Compaction algorithm and a level parameter which is initialized to 0 if nothing else is passed. The algorithm returns either a string representing the best matching term or a JSON object consisting of a propertyGenerators member, containing a sorted array of potential property generators, and an optional term member containing a term to fall back to if none of the property generators can be applied.

  1. Initialize result to null.
  2. If level equals 3, i.e., the deepest nested JSON object in the inverse context has been reached, perform the following steps:
    1. If inverseContextSubtree has a propertyGenerators member, copy that member into a new JSON object and store that object in result.
    2. If inverseContextSubtree has a term member and result is null, return the value of that member; otherwise result is a JSON object, copy the term member into result and return result.
  3. Otherwise, for each entry of the array in queryPath's level'd position (queryPath[level]), perform the following steps:
    1. If inverseContextSubtree has no entry member, continue with the next entry.
    2. Otherwise set tmpResult to the result of recursively invoking this algorithm passing the entry member of inverseContextSubtree as new inverseContextSubtree, queryPath, and level + 1 as new level.
    3. If tmpResult is a string and result is null, return tmpResult; otherwise, if result is not null, transform tmpResult to a JSON object whose propertyGenerators member is set to an empty array and whose term member is set to tmpResult.
    4. If result is null, replace it with tmpResult.
    5. Otherwise, append each item of the propertyGenerator member of tmpResult to the propertyGenerator member of result unless it is already in result's member. If result has no term member but tmpResult does, copy tmpResult's term member into result.
  4. Return result.

5.12 Value Compaction

Expansion transforms all values into expanded formin JSON-LD. This algorithm does the opposite, it compacts an algorithm according the term definition of the passed active property. After the application of this algorithm a value is said to be in compacted form.

This algorithm takes a value, an active context, an inverse context, an active property, and an optional containerValue.

  1. If a containerValue has been passed, the active property has a container mapping set to @annotation and value has an @annotation member which equals containerValue, remove that member.
  2. If value is a JSON object having a single member @id, return the result of performing IRI Compaction on that member's value.
  3. Otherwise, if value is a JSON object having a @value member, perform the following steps:
    1. If the active property has a type mapping and value has a corresponding @type member, remove the @type member from value.
    2. Otherwise, if the active property has a language mapping and value has a corresponding @language member, remove the @language member from value.
    3. Otherwise, if a containerValue has been passed, the active property has a container mapping set to @language, and value has a @language member which equals containerValue, remove the @language member from value.
    4. Otherwise, if the active context has a default language
      1. and the value has a corresponding @language member, remove the @language member from value.
      2. Otherwise, or if the value of value's @value member is a string, return value.
    5. If value has just a @value member, return the value of that member.
  4. Return value as is.

5.13 Find and Remove Property Generator Duplicates

This algorithm checks if a specific value exists for all IRIs associated with a property generator and if so, it removes them. The algorithm takes five parameters: element, property, value, active context, active property.

  1. For each item propertyGenerator of the array which is the value of the propertyGenerators member of active property perform the following steps:
    1. Check that a member exists for each IRI associated with the propertyGenerator. If value is not null also check whether each of those members that contains value. Values are considered to be equal if they are of the same type and have the same value(s); node objects are considered to be equal if all members match, except if no @id member exists (i.e., it is an unlabeled blank node, in that case node objects are never considered to be equal.
    2. If that's not the case, continue with the next propertyGenerator.
    3. Otherwise, remove value from every member. If the resulting value of a member is an empty array, remove the member altogether from element.
    4. Return propertyGenerator.
  2. Return the value of the term member of active property since no matching property generator has been found.

5.14 Flattening Algorithm

The algorithm takes two input variables, an element to flatten and a context used to compact the flattened document.

  1. Expand element according the Expansion algorithm.
  2. Generate a nodeMap according the Node Map Generation algorithm.
  3. Let defaultGraph be the value of the @default member of nodeMap; a JSON object representing the default graph.
  4. For each other graphName-graph pair in nodeMap perform the following steps:
    1. If defaultGraph does not have a graphName member, create one and initialize its value to a JSON object consisting of an @id member whose value is set to graphName.
    2. Add a @graph member set to an empty array (referred to as nodes) to the JSON object which is the value of the graphName member of nodeMap.
    3. For each id-node pair in graph ordered by id, add node to the nodes array.
  5. Initialize an empty array flattened.
  6. For each id-node pair in defaultGraph ordered by id, add node to flattened.
  7. If context equals null, return flattened.
  8. Otherwise, return the result of compacting flattened according the Compaction algorithm passing context ensuring that the compaction result uses the @graph keyword (or its alias) at the top-level, 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.

5.15 Node Map Generation

This algorithm creates a JSON object nodeMap 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 nodeMap 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.

The algorithm takes as input an expanded JSON-LD document element and a reference to a JSON object nodeMap. Furthermore it has the optional parameters active graph (which defaults to @default), an active subject, active property, and a reference to a JSON object list. The nodeMap must be initialized to a JSON object consisting of a single member whose name corresponds with active graph and whose value is an empty JSON object.

  1. If element is an array, process each entry in element recursively, using this algorithm and return.
  2. Otherwise element is a JSON object. Let activeGraph be the JSON object which is the value of the active graph member of nodeMap.
  3. If it has a @type member, perform for each item the following steps:
    1. If item is a blank node identifier, replace it with a new blank node identifier.
    2. If activeGraph has no member item, create it and initialize its value to a JSON object consisting of a single member @id with the value item.
  4. If element has an @value member, perform the following steps:
    1. If active property is null, generate a blank node identifier id and store element as value of the member id in activeGraph.

      Handling of free-floating values is still being discussed.

    2. Otherwise, if no list has been passed, merge element into the active property member of the active subject in activeGraph.
    3. Otherwise, append element to the @list member of list.
  5. Otherwise, if element has an @list member, perform the following steps:
    1. Initialize a new JSON object result having a single member @list whose value is initialized to an empty array.
    2. Recursively call this algorithm passing the value of element's @list member as new element and result as list.
    3. If active property is null, generate a blank node identifier id and store result as value of the member id in activeGraph.

      Handling of free-floating values is still being discussed.

    4. Otherwise, add result to the the value of the active property member of the active subject in activeGraph.
  6. Otherwise element is a node object, perform the following steps:
    1. If element has an @id member, store its value in id and remove the member from element. If id is a blank node identifier, replace it with a new blank node identifier.
    2. Otherwise generate a new blank node identifier and store it as id.
    3. If activeGraph does not contain a member id, create one and initialize it to a JSON object consisting of a single member @id whose value is set to id.
    4. If active property is not null, perform the following steps:
      1. Create a new JSON object reference consisting of a single member @id whose value is id.
      2. If no list has been passed, merge element into the active property member of the active subject in activeGraph.
      3. Otherwise, append element to the @list member of list.
    5. If element has an @type member, merge each value into the @type of active subject in activeGraph. Then remove the @type member from element.
    6. If element has an @annotation member, set the @annotation of active subject in activeGraph to its value. If such a member already exists in active subject and has a different value, raise a CONFLICTING_ANNOTATION error. Otherwise continue and remove the @annotation from element.
    7. If element has an @graph member, recursively invoke this algorithm passing the value of the @graph member as new element and id as new active subject. Then remove the @graph member from element.
    8. Finally for each property-value pair in element ordered by property perform the following steps:
      1. If no property member exists in the JSON object which is the value of the id member of activeGraph create the member and initialize its value to an empty array.
      2. Recursively invoke this algorithm passing value as new element, id as new active subject, and property as new active property.

5.16 RDF Conversion Algorithms

This specification describes algorithms to transform JSON-LD documents to an array of RDF quads and vice-versa. Note that many uses of JSON-LD may not require generation of RDF.

The processing algorithms described in this section are provided in order to demonstrate how one might implement a JSON-LD to RDF processor. Conformant implementations are only required to produce the same type and number of quads but are not required to implement the algorithm exactly as described.

Issue

This algorithm hasn't been updated yet.

5.16.1 Convert to RDF Algorithm

The algorithm below is designed for in-memory implementations with random access to JSON object elements.

A conforming JSON-LD processor implementing RDF conversion must implement a processing algorithm that results in the same set of RDF quads that the following algorithm generates:

The algorithm takes four input variables: a element to be converted, an active subject, active property and graph name. To begin, the active subject, active property and graph name are set to null, and element is set to the result of performing the Expansion Algorithm on the JSON-LD input which is expected to be a a well-formed JSON-LD document as defined in [JSON-LD]. This removes any existing context to allow the given context to be cleanly applied.

  1. If element is a JSON object, perform the following steps:
    1. Set active object to null.
    2. If element has a @value property:
      1. If the value of @value is a number, set the active object to a typed value using a canonical lexical form of the value as defined in the section Data Round Tripping. Set datatype to the value of the @type property if it exists, otherwise either xsd:integer or xsd:double, depending on if the value contains a fractional and/or an exponential component.
      2. Otherwise, if the value of @value is true or false, set the active object to a typed value created from the canonical lexical form of the value. Set datatype to the value of the @type property if it exists, otherwise xsd:boolean.
      3. Otherwise, if element contains a @type property, set the active object to a typed value.
      4. Otherwise, if element contains a @language property, set the active object to a language-tagged string.
      5. Otherwise, set the active object to a typed value using xsd:string as the datatype.
    3. If element has a @list property the value must be an array. Process its value as a list as described in List Conversion using the return value as the active object
    4. If active object is not null:
      1. If neither active subject nor active property are null, generate a Quad representing active subject, active property, active object, and graph name.
      2. Return active object.
    5. If element has a @id property, the value must be a string, set the active subject to the previously expanded value (either a blank node or an IRI).
    6. Otherwise, if element does not have a @id property, set the active subject to newly generated blank node.
    7. Process each property and value in element, ordered by property, as follows:
      1. If property is @type, set the active property to rdf:type.
      2. Otherwise, if property is @graph, process value algorithm recursively, using active subject as graph name and null values for active subject and active property and then proceed to next property.
      3. Otherwise, if property is a keyword, skip this step.
      4. Otherwise, set active property to the expanded IRI form of property.
      5. Process value recursively using this algorithm, passing copies of active subject, active property and graph name.
    8. Set active object to active subject.
  2. Otherwise, if element is an array, process each value in the array as follows, process element recursively using this algorithm, using copies of active subject, active property, and graph name.
  3. Otherwise, if element is a string, then the active property must be rdf:type so set the active object to an IRI.
  4. If any of these steps created an active object and neither active subject nor active property are null, generate a Quad using active subject,active property, active object and graph name.
  5. Return active object.

5.16.2 List Conversion

List Conversion is the process of taking an array of values and adding them to a newly created RDF Collection (see [RDF-SCHEMA]) by linking each element of the list using rdf:first and rdf:next, terminating the list with rdf:nil using the following sequence:

The algorithm is invoked with an array array, the active property and returns a value to be used as an active object in the calling location.

Note
This algorithm does not support lists containing lists.
Issue

This algorithm hasn't been updated yet.

  1. If array is empty return rdf:nil.
  2. Otherwise, generate a Quad using using the active subject, active property and a newly generated blank node identified as first blank node.
  3. For each element in array other than the last element:
    1. Create a processor state using first blank node as the active subject, and rdf:first as the active property.
      1. Process the value starting at Step 1.
      2. Proceed using the previous processor state.
    2. Unless this is the last element in array, generate a new blank node identified as rest blank node, otherwise use rdf:nil.
    3. Generate a new Quad using first blank node, rdf:rest and rest blank node.
    4. Set first blank node to rest blank node.
    5. Return first blank node.

5.16.3 Convert from RDF Algorithm

In some cases, data exists natively in the form of triples or or quads; for example, if the data was originally represented in an RDF graph or triple/quad store. This algorithm is designed to simply translate an array of quads into a JSON-LD document.

When expanding typed values having a datatype of xsd:string, the @type must not be set to xsd:string and the resulting value must have only a @value property.

The conversion algorithm takes a single parameter input in the form of an array of Quad representations.

Issue

This algorithm hasn't been updated yet.

  1. Construct defaultGraph as a JSON object containing nodes and listMap, each an empty JSON object.
  2. Construct graphs as a JSON object containing defaultGraph identified by an empty string.
  3. For each quad in input:
    1. Set graph to the entry in graphs identified by name, initializing it to a new entry using the mechanism described in Step 1.
    2. If property is rdf:first, use the entry in graph.listMap indexed by subject, initializing it to a new JSON object if nesessary. Represent object in expanded form, as described in Value Expansion. Add the resulting object representation to the entry indexed by first, and skip to the next quad.
    3. If property is rdf:rest:
      1. If object is a blank node, use the entry in graph.listMap indexed by subject, initializing it to a new JSON object if necessary. Add the nominalValue of object to the entry indexed by rest.
      2. Skip to the next quad.
    4. If name is not null, and defaultGraph.nodes does not contain an entry for name, create a new entry for name from a new JSON object with key/value pair of @id and name represented in expanded IRI form.
    5. Set value as the entry from graph.nodes for subject, initializing it to a new JSON object with key/value pair of @id and subject represented in expanded IRI form if necessary.
    6. If property is rdf:type, object is not a JSON-LD value, and the useRdfType option is not present or false:
      1. Append object represented in expanded IRI form to the array value for the key @type, creating an entry in value if necessary.
    7. Otherwise, if object is a typed value and the useNativeTypes option is set to true:
      1. Generate a converted value:
        1. If the literal's type is xsd:boolean, the converted value is true if the literal matches the value true or false if the literal matches the value false.
        2. If the literal's type is xsd:integer or xsd:double, try to convert the literal to a JSON number. If the conversion is successful, store the result in converted value, otherwise set converted value to value.
        3. Otherwise, do not perform a conversion. Set the converted value to the value.
      2. Append the converted value to the array value for the key, creating an entry in value if necessary.
    8. Otherwise, if object is rdf:nil:
      1. Let key be property expressed in expanded IRI form.
      2. Append an empty @list representation to the array value for key, creating an entry in value if necessary.
    9. Otherwise,
      1. Let key be property expressed in expanded IRI form and let object representation be object represented in expanded form as described in Value Expansion.
      2. If object is a blank node, use the entry in graph.listMap indexed by object, initializing it to a new JSON object if nesessary. Add an entry for head with object representation.
      3. Append object representation to the array value for key, creating an entry in value if necessary.
  4. For each name and graph in graphs:
    1. For each subject and entry in graph where entry has both head and first keys:
      1. Set value to the value of head in entry.
      2. Remove the entry for @id in value.
      3. Add an entry to value for @list initialized to a new array containing the value of first from entry.
      4. While entry has a key for rest:
        1. Set entry to the value of graph.listMap for entry.rest.
        2. Add the value for entry.first to the list array.
  5. Create array as an empty array.
  6. For each subject and entry in defaultGraph.nodes ordered by subject:
    1. Add entry to array.
    2. If graphs has an entry for subject, add a property @graph in entry containing the ordered entries from graphs[subject].nodes.
  7. Return array as the result.

5.16.4 Data Round Tripping

When converting JSON-LD to RDF JSON-native types such as numbers and booleans are automatically coerced to xsd:integer, xsd:double, or xsd:boolean. Implementers must ensure that the result is in canonical lexical form. A canonical lexical form is a set of literals from among the valid set of literals for a datatype such that there is a one-to-one mapping between the canonical lexical form and a value in the value space as defined in [XMLSCHEMA11-2]. In other words, every value must be converted to a deterministic string representation.

The canonical lexical form of an integer, i.e., a number without fractions or a number coerced to xsd:integer, is a finite-length sequence of decimal digits (0-9) with an optional leading minus sign; leading zeroes are prohibited. To convert the number in JavaScript, implementers can use the following snippet of code:

Example 12
(value).toFixed(0).toString()

The canonical lexical form of a double, i.e., a number with fractions or a number coerced to xsd:double, consists of a mantissa followed by the character "E", followed by an exponent. The mantissa must be a decimal number. The exponent must be an integer. Leading zeroes and a preceding plus sign (+) are prohibited in the exponent. If the exponent is zero, it must be indicated by E0. For the mantissa, the preceding optional plus sign is prohibited and the decimal point is required. Leading and trailing zeroes are prohibited subject to the following: number representations must be normalized such that there is a single digit which is non-zero to the left of the decimal point and at least a single digit to the right of the decimal point unless the value being represented is zero. The canonical representation for zero is 0.0E0. xsd:double's value space is defined by the IEEE double-precision 64-bit floating point type [IEEE-754-1985]; in JSON-LD the mantissa is rounded to 15 digits after the decimal point.

To convert the number in JavaScript, implementers can use the following snippet of code:

Example 13
(value).toExponential(15).replace(/(\d)0*e\+?/,'$1E')
Note

When data such as decimals need to be normalized, JSON-LD authors should not use values that are going to undergo automatic conversion. This is due to the lossy nature of xsd:double values. Authors should instead use the expanded object form to set the canonical lexical form directly.

The canonical lexical form of the boolean values true and false are the strings true and false.

When JSON-native numbers, are type coerced, lossless data round-tripping can not be guaranted as rounding errors might occur. Additionally, only literals typed as xsd:integer, xsd:double, and xsd:boolean are automatically converted back to their JSON-native counterparts in when converting from RDF.

Some JSON serializers, such as PHP's native implementation in some versions, backslash-escape the forward slash character. For example, the value http://example.com/ would be serialized as http:\/\/example.com\/. This is problematic as other JSON parsers might not understand those escaping characters. There is no need to backslash-escape forward slashes in JSON-LD. To aid interoperability between JSON-LD processors, a JSON-LD serializer must not backslash-escape forward slashes.

6. The Application Programming Interface

This API provides a clean mechanism that enables developers to convert JSON-LD data into a a variety of output formats that are easier to work with in various programming languages. If a JSON-LD API is provided in a programming environment, the entirety of the following API must be implemented.

6.1 JsonLdProcessor

The JSON-LD processor interface is the high-level programming structure that developers use to access the JSON-LD transformation methods.

The JSON-LD API signatures are the same across all programming languages. Due to the fact that asynchronous programming is uncommon in certain languages, developers may implement a processor with a synchronous interface instead. In that case, the callback parameter must not be included and the result must be returned as a return value instead.

It is important to highlight that conformant JSON-LD processors must not modify the input parameters.

[Constructor]
interface JsonLdProcessor {
    void expand ((object or object[] or DOMString) input, JsonLdCallback callback, optional JsonLdOptions? options);
    void compact ((object or object[] or DOMString) input, (object or DOMString) context, JsonLdCallback callback, optional JsonLdOptions? options);
    void flatten ((object or object[] or DOMString) input, (object or DOMString)? context, JsonLdCallback callback, optional JsonLdOptions? options);
};

6.1.1 Methods

compact
Compacts the given input using the context according to the steps in the Compaction Algorithm.
ParameterTypeNullableOptionalDescription
input(object or object[] or DOMString)The JSON-LD object or array of JSON-LD objects to perform the compaction upon or an IRI referencing the JSON-LD document to compact.
context(object or DOMString)The context to use when compacting the input; either in the form of an JSON object or as IRI.
callbackJsonLdCallbackA callback that is called when processing is complete on the given input.
optionsJsonLdOptionsA set of options to configure the used algorithms such. This allows, e.g., to set the input document's base IRI. This also includes the optimize flag, which, if set, will allow processor-specific optimization.
Return type: void
expand
Expands the given input according to the steps in the Expansion Algorithm.
ParameterTypeNullableOptionalDescription
input(object or object[] or DOMString)The JSON-LD object or array of JSON-LD objects to perform the expansion upon or an IRI referencing the JSON-LD document to expand.
callbackJsonLdCallbackA callback that is called when processing is complete on the given input.
optionsJsonLdOptionsA set of options to configure the used algorithms such. This allows, e.g., to set the input document's base IRI.
Return type: void
flatten
Flattens the given input and compacts it using the passed context according to the steps in the Flattening Algorithm.
ParameterTypeNullableOptionalDescription
input(object or object[] or DOMString)The JSON-LD object or array of JSON-LD objects or an IRI referencing the JSON-LD document to flatten.
context(object or DOMString)The context to use when compacting the flattened input; either in the form of an JSON object or as IRI. If null is passed, the result will not be compacted but keept in expanded form.
callbackJsonLdCallbackA callback that is called when processing is complete on the given input.
optionsJsonLdOptionsA set of options to configure the used algorithms such. This allows, e.g., to set the input document's base IRI.
Return type: void

6.2 Callbacks

JSON-LD processors utilize callbacks in order to return information in an asynchronous manner to calling applications. This section details the parameters sent to those callbacks.

6.2.1 JsonLdCallback

The JsonLdCallback is called when processing of an API method of JsonLdProcessor has been completed successfully or been terminated by an error.

callback JsonLdCallback = void (JsonLdProcessingError error, object or object[] document);
Callback JsonLdCallback Parameters
error of type JsonLdProcessingError
If the value is null, then no error occurred. If the value is non-null, a processing error occurred and the details will be contained within the error object.
document of type array of object or object
The processed JSON-LD document.

6.3 Data Structures

This section describes datatype definitions used within the JSON-LD API.

6.3.1 JsonLdOptions

The JsonLdOptions type is used to pass various options to the JsonLdProcessor methods.

dictionary JsonLdOptions {
    DOMString           base;
    object or DOMString expandContext = null;
    boolean             compactArrays = true;
    boolean             optimize = false;
    boolean             useRdfType = false;
    boolean             useNativeTypes = true;
};
Dictionary JsonLdOptions Members
base of type DOMString
The Base IRI to use when expanding the document. This overrides the value of input if it is a IRI. If not specified and input is not an IRI, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context.
compactArrays of type boolean, defaulting to true
If set to true, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to false, all arrays will remain arrays even if they have just one element.
expandContext of type object or DOMString, defaulting to null
A context that is used to initialize the active context when expanding a document.
optimize of type boolean, defaulting to false
If set to true, the JSON-LD processor is allowed to optimize the output of the Compaction Algorithm to produce even compacter representations. The algorithm for compaction optimization is beyond the scope of this specification and thus not defined. Consequently, different implementations may implement different optimization algorithms.
useNativeTypes of type boolean, defaulting to true
If set to true, the JSON-LD processor will try to convert typed values to JSON native types instead of using the expanded object form when converting from RDF. xsd:boolean values will be converted to true or false. xsd:integer and xsd:double values will be converted to JSON numbers.
useRdfType of type boolean, defaulting to false
If set to true, the JSON-LD processor will use the expanded rdf:type IRI as the property instead of @type when converting from RDF.

6.3.2 JsonLdProcessingError

Developers should note that the details of error handling are being actively debated.

The JsonLdProcessingError type is used to report errors to a JsonLdCallback.

dictionary JsonLdProcessingError {
    JsonLdErrorCode code;
    DOMString?      message;
};
Dictionary JsonLdProcessingError Members
code of type JsonLdErrorCode
a string representing the particular error type, as described in the various algorithms in this document.
message of type DOMString, nullable
an optional error message containing additional debugging information. The specific contents of error messages are outside the scope of this specification and thus implementation dependent.

6.3.3 JsonLdErrorCode

The JsonLdErrorCode represent the collection of valid JSON-LD error codes.

enum JsonLdErrorCode {
    "INVALID_SYNTAX",
    "LOAD_ERROR",
    "LIST_OF_LISTS_DETECTED"
};
Enumeration description
INVALID_SYNTAXA violation of the grammar as defined by the JSON-LD syntax specification [JSON-LD] was detected.
LOAD_ERRORThere was a problem encountered loading a remote context.
LIST_OF_LISTS_DETECTEDA list of lists was detected. List of lists are not supported in this version of JSON-LD due to the algorithmic complexity associated with conversion to RDF.

A. Acknowledgements

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 or their input on the specification.

B. References

B.1 Normative references

[IEEE-754-1985]
IEEE. IEEE Standard for Binary Floating-Point Arithmetic. See http://standards.ieee.org/reading/ieee/std_public/description/busarch/754-1985_desc.html
[JSON-LD]
The JSON-LD Syntax Manu Sporny, Gregg Kellogg, Markus Lanthaler Editors. World Wide Web Consortium (work in progress). 22 May 2012. Editor's Draft. This edition of the JSON-LD Syntax specification is http://json-ld.org/spec/ED/json-ld-syntax/20120522/. The latest edition of the JSON-LD Syntax is available at http://json-ld.org/spec/latest/json-ld-syntax/
[RDF-CONCEPTS]
RDF 1.1 Concepts and Abstract Syntax Richard Cyganiak, David Wood, Editors. World Wide Web Consortium (work in progress). 30 May 2012. Editor's Draft. This edition of the JSON-LD Syntax specification is http://www.w3.org/TR/2011/WD-rdf11-concepts-20110830/. The latest edition of the JSON-LD Syntax is available at http://www.w3.org/TR/rdf11-concepts/
[RDF-SCHEMA]
Dan Brickley; Ramanathan V. Guha. RDF Vocabulary Description Language 1.0: RDF Schema. 10 February 2004. W3C Recommendation. URL: http://www.w3.org/TR/2004/REC-rdf-schema-20040210
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Internet RFC 2119. URL: http://www.ietf.org/rfc/rfc2119.txt
[RFC3986]
T. Berners-Lee; R. Fielding; L. Masinter. Uniform Resource Identifier (URI): Generic Syntax. January 2005. Internet RFC 3986. URL: http://www.ietf.org/rfc/rfc3986.txt
[RFC3987]
M. Dürst; M. Suignard. Internationalized Resource Identifiers (IRIs). January 2005. Internet RFC 3987. URL: http://www.ietf.org/rfc/rfc3987.txt
[RFC4627]
D. Crockford. The application/json Media Type for JavaScript Object Notation (JSON) July 2006. Internet RFC 4627. URL: http://www.ietf.org/rfc/rfc4627.txt
[WEBIDL]
Web IDL Cameron McCormack, Editor. World Wide Web Consortium. 19 April 2012. Candidate Recommendataion. This edition of Web IDL is http://www.w3.org/TR/2012/CR-WebIDL-20120419/. The latest edition of Web IDL is available at http://dev.w3.org/2006/webapi/WebIDL/
[XMLSCHEMA11-2]
Henry S. Thompson; et al. W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes. 5 April 2012. W3C Recommendation URL: http://www.w3.org/TR/2012/REC-xmlschema11-2-20120405/

B.2 Informative references

[BCP47]
A. Phillips; M. Davis. Tags for Identifying Languages September 2009. IETF Best Current Practice. URL: http://tools.ietf.org/html/bcp47
[ECMA-262]
ECMAScript Language Specification. June 2011. URL: http://www.ecma-international.org/publications/standards/Ecma-262.htm
[JSON-LD-TESTS]
JSON-LD Test Suite (work in progress).
[TURTLE-TR]
Eric Prud'hommeaux, Gavin Carothers. Turtle: Terse RDF Triple Language. 09 August 2011. W3C Working Draft. URL: http://www.w3.org/TR/2011/WD-turtle-20110809/