This NOTE describes an extension to [[JSON-LD11]], [[JSON-LD11-API]], and [[JSON-LD11-FRAMING]] to allow arcs in a Linked Data Graph to be annotated using the [[RDFStar]] model.

This is an unofficial proposal.

Introduction

[[[RDFStar]]] [[RDFStar]] addresses the problem of annotating arcs in a Linked Data Graph by extending the RDF data model [[RDF11-CONCEPTS]] to allow for triples to be used as the subject or object of another RDF triple. This note describes an update to the JSON-LD data model to allow limited form of a node object to use another node object as its node identifier.

The two popular graph-based data models have been RDF [[RDF11-CONCEPTS]] and Labeled-property Graphs, which are roughly similar, with the RDF model being more formal in identifying nodes, datatypes and relationships, while Property Graphs use a less formal relationship model somewhat similar to JSON. In both models, nodes are related via edges (AKA arcs), but in Property Graphs, those edges may themselves be annotated with properties. This is useful in providing additional metadata and semantics to relationships of the nodes.

Historically, in RDF, this can be simulated through Reification, where a triple is represented by another resource with properties for the subject, predicate, and object, which allows additional properties to be asserted on that reification node.

      
      

In [[RDFStar]], a triple may act as the subject or object in another triple, for example, we can modify how certain we are about a relationship as follows:

      
      

This NOTE explores an extension of JSON-LD which can allow the value of an `@id` property to be an embedded node, and the description of an annotation object which serves as a short-hand when the annotated value also is described directly in the graph.

A JSON-LD-star document complies with this specification if it follows the normative statements in . For convenience, normative statements for documents are often phrased as statements on the properties of the document.

This specification makes use of the following namespace prefixes:

Prefix IRI
ex http://example.org/
rdf http://www.w3.org/1999/02/22-rdf-syntax-ns#
xsd http://www.w3.org/2001/XMLSchema#

These are used within this document as part of a compact IRI as a shorthand for the resulting IRI, such as dcterms:title used to represent http://purl.org/dc/terms/title.

JSON-LD-star-specific Terms

The Following terms are used within specific algorithms.

RDF-star dataset
An RDF dataset extended to allow the use of an RDF-star graph instead of a plain RDF graph.
RDF-star graph
A set of RDF-star triples.
RDF-star triple
An RDF triple extended to allow an RDF-star triple to be used in the subject or object position.
Embedded Node
An embedded node is a node object describing exactly one property/value pair. The node object MAY be identified as with other node objects (including with an embedded node). The property MUST be either `@type` or an IRI. The property value MUST be either an IRI, blank node, literal or an embedded node. An embedded node MAY contain other entries that do not, themselves, create directed-arcs, such as `@context` or `@index`. An embedded triples is the representation of an embedded triple.
JSON-LD-star document
A JSON-LD-star document is a serialization of an RDF dataset as extended by [[RDFStar]].
Annotation Object
An annotation object is a node object which is the value of `@annotation` (or an alias). It describes [[RDFStar]] annotations made on the triple between by its parent node object or value object, and the parent's closest ancestor node object.

Basic Concepts

This specification adds the concepts of an embedded node and annotation object to JSON-LD in order to add the ability to make statements about individual triples in an RDF-star Graph.

An embedded node supports the ability to make one or more statements about a triple, represented by an embedded node, without that triple being necessarily considered to be part of the Linked Data Graph.

The following example shows how the value of `@id` can be an of embedded node, which expresses a single triple:

An annotation object supports the ability to make one or more statements (annotations) about a triple, where the triple is considered to be part of the Linked Data Graph.

Advanced Concepts

JSON-LD allows many more sophisticated ways of describing data and both embedded nodes and annotation objects work along with these other features, to the extent that they are used in describing a single relationship.

Reverse Properties

JSON-LD allows the description of within node objects, either by defining a term using `@reverse`, or using `@reverse` directly within a node object.

An embedded node MUST NOT, itself, use a reverse property.

The relationships between nodes can be reversed even when those nodes use embedded objects:

Annotations may themselves use reverse properties, in which case the annotated triple will be the object (instead of the subject) of the annotation:

Deep Embedding

[[RDFstar]] allows embedded triples to contain other embedded triples, recursively. Similarly, an embedded node may be made up of other embedded nodes, and annotations may be made upon embedded nodes.

Mixing Annotations and Embedded Nodes

Annotation objects and embedded nodes can be used together. As well, annotation objects can be used to annotate embedded nodes (see ), embedded nodes may be used within annotation objects, and annotation objects may be used to annotate annotations. However, the grammar prevents embedded nodes from including annotation objects.

While it is beyond the scope of this document to discuss use cases for these scenarios, it is an important notion of regularity of expression, which is shared by formats such as Turtle-star.

Consider the following examples:

More examples can be found in the [[[JSON-LD-star-tests]]].

Data Model

The JSON-LD Data Model is extended consistent with [[RDFStar]]:

Embedded Nodes can be considered as reified triples.

Align with [[RDFStar]].

Grammar Extensions

The is extended as follows:

Algorithmic Extensions

This specification extends the algorithms from [[[JSON-LD11-API]]] [[JSON-LD11-API]] to support the addition of embedded nodes and annotation objects.

The following algorithm extensions require that the rdfstar flag be set.

Expansion Algorithm

The algorithm has minor updates to allow for an embedded node to be used as the value of `@id`, and to allow and validate the special requirements for annotation objects.

Expansion does not change annotations into embedded nodes, neither does it reverse node relationships. This is performed by other algorithms.

Before step 13.4.3.1, and changing 13.4.3.1 to be an "Otherwise, if", add the following steps:

  1. If the rdfstar flag is not set, and active property is `@annotation`, an invalid annotation error has been detected and processing is aborted.
  2. If value is a non-empty map and the rdfstar flag is set, set expanded value to the result of using this algorithm recursively passing active context, `null` for active property, value for element, base URL, and the frameExpansion and ordered flags. The resulting expanded value MUST BE a valid embedded node, otherwise, an invalid embedded node error has been detected and processing is aborted.

After step 13.4.14 add the following step:

  1. If expanded property is `@annotation:
    1. Continue with the next key from element if the rdfstar flag is not set.
    2. Set expanded value to the result of using this algorithm recursively passing active context, `@annotation` for active property, value for element, base URL, and the frameExpansion and ordered flags, ensuring that the result is in an array.

Step 15.1 extends the set of allowed entries to include `@annotation`.

Before step 18 add the following step:

  1. If result contains the entry `@annotation` all associated values MUST BE annotation objects, otherwise, an invalid annotation error has been detected and processing is aborted. Additionally, if there is no active property, or active property is `@graph` or `@included`, the annotation appears in an inappropriate location and an invalid annotation error has been detected and processing is aborted.

Compaction Algorithm

The Compaction Algorithm has minor updates to allow for an embedded node to be used as the value of `@id`, and to allow embedded nodes and annotation objects. Values that might otherwise be serialized as a string must stay as a map if they contain an annotation object.

Step 7 is updated to exclude elements haveing an `@annotation` entry.

After step 12.1.1, add the following steps:

  1. Otherwise, if expanded value is an embedded node, initialize compacted value with the result of calling this algorithm, recursively, passing active context, `null` for active property, expanded value for element, and the compactArrays and ordered flags.

Flattening Algorithms

The flattening algorithms are updated to allow the use of embedded nodes, and to turn annotation objects into embedded nodes.

There is a new Create Annotations Algorithm optionally invoked using the createAnnotations API flag, which will re-create most annotation objects within a node map.

Flattening Algorithm

The main changes to the Flattening Algorithm is to move the blank node regeneration to a single recursive step run prior to running the Node Map Generation algorithm. This is required to consistently regenerate blank node identifiers used within embedded nodes, which are otherwise untouched by the algorithm.

Before step 2 add the following step:

  1. Update element using the Rename Blank Nodes algorithm passing element.

Before step 3 add the following step:

  1. If the createAnnotations flag is set:
    1. For each key-value pair graph name-graph in node map, perform the following steps:
      1. Update graph in node map by invoking the Create Annotations Algorithm passing graph for node map.

Node Map Generation

Node Map Generation Algorithm has significant updates due to the requirements of unfolding annotation objects into embedded nodes and representing embedded nodes as keys in the node map. Also, some assumptions are made in the original algorithm that the active subject being a map implies a reverse relationship, which is no longer the case when an embedded node may be the active subject.

Principle changes include the following:

  • As indicated in , most blank node regeneration is performed prior to invoking this algorithm, rather than inline. The exception is for node objects without an explicit `@id`, and where a blank node is used as a property.
  • A new argument reverse is introduced which is passed when processing an `@reverse` map.
  • If an `@annotation` key is encountered, it creates an embedded node based on the active subject and the map containing the `@annotation` key which is used in a recursive invokation of this algorithm on the contents of the annotation object.
  • If the active subject is an embedded node, the result of the [[[RFC8785]]] [[RFC8785]] is used as an index into the node map.

A new optional parameter reverse is added defaulting to `false`.

In step 2, when referencing the active subject entry of graph, if active subject is a map, index using the canonical lexical form of active subject. Additionally, do use `null` if reverse is `false`.

Before step 4.1.1, ad the following steps:

  1. If element has an `@annotation` entry:
    1. Initialize annotation subject as a new map with an entry for `@id` taken from active subject. If active subject is a node reference, use value of `@id` from active subject.
    2. Add an entry for active property with the value an array containing element, after removing the `@annotation` value from element.
    3. Initialize annotation as an array containing the values from the `@annotation` entry in element adding an `@id` entry to each with the value annotation subject.
    4. Recursively call this algorithm passing annotation for element, node map, and active graph.

Replace 6.3 and 6.4 with the following steps:

  1. If id is a map, set serialized id to the canonical lexical form of id, otherwise to id.
  2. If graph does not contain an entry serialized id, create one and initialize its value to a map consisting of a single entry @id whose value is id.
  3. Reference the value of the serialized id entry of graph using the variable node.

Change the condition for step 6.5 to check the reverse parameter for `true` rather than if active subject is a map.

Before step 6.8, ad the following steps:

  1. If element has an `@annotation` entry:
    1. Initialize annotation subject as a new map.
    2. If reverse is `true`:
      1. Add an entry for `@id` with the value id.
      2. Add an entry for active property with the value from active subject. If active subject is a node reference, use value of `@id` from active subject.
    3. Otherwise:
      1. Add an entry for `@id` taken from active subject. If active subject is a node reference, use value of `@id` from active subject.
      2. Add an entry for active property with the value id.
    4. Initialize annotation as an array containing the values from the `@annotation` entry in element adding an `@id` entry to each with the value annotation subject.
    5. Recursively call this algorithm passing annotation for element, node map, and active graph.

Update step 6.9.3.1.1 to include a reverse parameter with the value `true`.

Rename Blank Nodes

This algorithm use used to comprehensively rename the blank node identifiers used within a map by recursively visiting each element of the map.

This will change the naming of renamed blank nodes used in existing tests. As indicated in the JSON-LD Test Suite:

When comparing documents after flattening, framing or generating RDF, blank node identifiers may not be predictable.

One way to do this is to transform both the generated results and the expected results into an RDF Dataset and perform a blank node bijection (as described for RDF Dataset Isomorphsim) and then use the resulting bijection to rename the blank nodes in the result based on those that are expected.

The algorithm takes a single input variable element and returns a representation of that element with blank node identifiers remapped.

  1. If element is an array:
    1. Initialize an empty array result.
    2. For each item in element add the result of invoking this algorithm recursively passing item for element.
  2. Otherwise, if element is a map:
    1. Initialize an empty map result.
    2. For each key-value pair in element:
      1. If key is `@id` and value if a blank node identifier, add an entry to result for `@id` with the value from the result of the Generate Blank Node Identifier algorithm passing value for identifier.
      2. Otherwise, add an entry to result for `@id` with the value from the result of calling this algorithm recursively using value for element.
  3. Otherwise, set result to element

Return result.

Create Annotations

This algorithm is used to find entries in a node map where the key is a canonicalized embedded node and that embedded node exists in non-embedded form in the node map and move them to be annotation objects under that node. This effectively reverses the process of unfolding annotation objects performed in . It is also used in .

The algorithm takes a single input variable node map and updates it in place.

  1. For each entry key/annotation in node map, where the first character of key is an `OPEN BRACE` (`{`), ordered by decreasing length:
    1. Initialize embedded from the value for the `@id` entry of annotation.
    2. Initialize id with the value of the `@id` entry from embedded, using the canonical lexical form if it is a map.
    3. Initialize node to the entry for id in node map. If there is no such entry, continue to the next entry in node map.
    4. Besides `@id`, embedded will have exactly one entry. Initialize property and value to the key and the first value from that entry.
    5. If node has an entry for property with a value value:
      1. Remove the key entry from node map.
      2. Remove the `@id` entry from annotation.
      3. Use add value to add annotation to the property property in node, using `true` for as array.
  2. Return node map.

RDF Serialization/Deserialization Algorithms

The RDF transformation algorithms have minimal changes to allow emit and consume RDF-star triples.

Deserialize JSON-LD to RDF Algorithm

The Deserialize JSON-LD to RDF Algorithm has minimal changes to allow an embedded triple to be used as the subject or object of an RDF-star triple, and to use annotation objects, where appropriate.

After step 1.3.1 add the following step:

  1. If subject is a string with the form of an embedded node. Transform it into an RDF-star triple using the Object to RDF Algorithm passing subject as item.

Object to RDF Algorithm

The Object to RDF algorithm has a minimal update to allow a serialized embedded node to be transformed into an RDF-star triple by invoking the Deserialize JSON-LD to RDF Algorithm recursively.

The first argument MAY be an embedded node in canonical lexical form.

Before Step 4 add the following steps:

  1. If item is a string where the first character is an `OPEN BRACE` (`{`) it MUST represent a serialized embedded node:
    1. Initialize dataset as a new empty RDF dataset.
    2. Transform item into a map by deserializing item as JSON.
    3. Invoke the Object to RDF Algorithm passing a new map with the entry `@default` and value a new map with a new entry using the value of `@id` from entry as key and entry as value. If the value of `@id` is not a string, use the canonical lexical form as the key.
    4. The only entry in dataset will be a single RDF-star triple in the defaultGraph. Return that RDF-star triple.

Serialize RDF as JSON-LD Algorithm

The Serialize RDF as JSON-LD Algorithm algorithm has updates to transform RDF-star triples used as the subject or object of another triple to be used as the index into a node map and represented as embedded nodes.

Change step 5.7.1 to the following steps:

  1. If subject is an RDF-star triple:
    1. Set embedded subject to the value of the `@id` entry from the result of using the RDF to Object Conversion algorithm passing subject.
    2. Set subject index to the result of using the canonical lexical form of embedded subject.
    3. If node map does not have a subject index entry, create one and initialize its value to a new map consisting of a single entry @id whose value is set to embedded subject.
  2. Otherwise, if node map does not have a subject entry, create one and initialize its value to a new map consisting of a single entry @id whose value is set to subject.

Before step 5.7.4, add the following steps:

  1. If object is an RDF-star triple:
    1. Set embedded object to the value of the `@id` entry from the result of using the RDF to Object Conversion algorithm passing object.
    2. Set object index to the result of using the canonical lexical form of embedded object.
    3. If node map does not have an object index entry, create one and initialize its value to a new map consisting of a single entry @id whose value is set to embedded object.

After step 6.4, add the following steps:

  1. Update graph object in graph map by invoking the Create Annotations Algorithm passing graph object for node map.

RDF to Object Conversion

The RDF to Object Conversion algorithm has updates to transform RDF-star triples into an embedded node.

Before step 2, add the following steps:

  1. Otherewise, if value is an RDF-star triple:
    1. Create a new map embedded node with the entry `@id` whose value is the result of calling this algorithm, recursively, passing the subject from value.
    2. If the predicate from value is `rdf:type`, and the useRdfType flag is not `true`, add an entry for `@type` whose value is an array containing the object from value.
    3. Otherwise, add an entry for predicate whose value is an array containing the result of calling this algorithm, recursively, passing the object from value.
  2. Return embedded node.