Skip to content

Changes in Community Group Drafts Targeted for 1.1

David I. Lehn edited this page Jan 27, 2018 · 3 revisions

Since it’s original publication in 2014, JSON-LD 1.0 has become an essential format for describing structured data on the World Wide Web. Thanks, in part, to the adoption by schema.org as a recommended format, its use on the Web has grown enormously, used by between 10% to 18.2% of all websites.

Additionally, JSON-LD is one of the formats supported by schema.org, in addition to Microdata and RDFa. JSON-LD is also a required format in the Linked Data platform.

Web Data Commons publication in November 2017
Of 26,271,491 domains crawled, 2,685,738 contained embedded JSON-LD 1.0.
W3Techs reports in January 2018
18.2% of all websites use JSON-LD 1.0.

In the time between its original publication by the RDF 1.1 Working Group, the documents have been maintained by the JSON for Linking Data Community Group which has had over 100 participants and filed around 100 issues.

In response, the CG has worked to update the specifications and test-suite, accepting over 90 pull requests for a pending CG 1.1 release. These issues generally relate to allowing more natural JSON idioms to be expressed using JSON-LD:

  • Require @version announcement to enable new features (PR 473).
  • Allow all values to be used in a map, through the use of a transparent @none index (PR 569).
  • Index values based on @id and @type (PR 449).
  • Allow named graphs to be supported as values without the use of the @graph keyword (PR 549).
  • Improve @container description to ensure values always take an array form (PR 503).
  • Allow contexts to be defined within term definitions (Scoped Contexts) that apply for values of that term (PR 445)
  • Allow for deeper nesting of properties and values without introducing semantics (PR 451).

Version Announcement

For backwards compatibility, version 1.1 must be specified to use new 1.1 features (may be through API).

{
  "@context":
  {
     "@version": 1.1,
     "schema": "http://schema.org/",
     "name": "schema:name",
     "body": "schema:articleBody",
     "words": "schema:wordCount",
     "post": {
       "@id": "schema:blogPost",
       "@container": "@id"
     }
  },
  "@id": "http://example.com/",
  "@type": "schema:Blog",
  "name": "World Financial News",
  
}

Data Maps (Keys not Arrays)

JSON-LD 1.0 introduced two concepts to access property values through a map (an object with keys referencing values, where the key is logically part of the value).

Language Indexing
Allow string values to be indexed by their language tag. When expanded, these become an array of values having associated strings and languages.
Data Indexing
These allow both literal values and object values to be indexed using an semantically meaningless index. When expanded, the keys become values of an `@index` element added to the expanded values.

Both of these mechanisms worked great for conforming values, but would not allow values without a language or index to be compacted in the same way. JSON-LD 1.1 introduces the @none index. For example:

{
  "@context": {
    "vocab": "http://example.com/vocab/",
    "label": {
      "@id": "vocab:label",
      "@container": "@language"
    }
  },
  "@id": "http://example.com/queen",
  "label": {
    "en": "The Queen",
    "de": [ "Die Königin", "Ihre Majestät" ],
    "@none": "The Queen"
  }
}

In this case values of label can be represented if they have an associated language, or if they don't. This example expands to the following:

[
  {
    "@id": "http://example.com/queen",
    "http://example.com/vocab/label": [
      {
        "@value": "The Queen"
      },
      {
        "@value": "Die Königin",
        "@language": "de"
      },
      {
        "@value": "Ihre Majestät",
        "@language": "de"
      },
      {
        "@value": "The Queen",
        "@language": "en"
      }
    ]
  }
]

Compacting this representation back will use the same Language Index, unless a term which is a better match for a string without a language is found, to aid in backwards compatibility.

The same @none indexing method works for Data Indexing as well.

Identifier Indexing

JSON-LD expands on the indexing theme to index nodes by their identifier (@id value).

{
  "@context":
  {
     "@version": 1.1,
     "schema": "http://schema.org/",
     "name": "schema:name",
     "body": "schema:articleBody",
     "words": "schema:wordCount",
     "post": {
       "@id": "schema:blogPost",
       "@container": "@id"
     }
  },
  "@id": "http://example.com/",
  "@type": "schema:Blog",
  "name": "World Financial News",
  "post": {
     "http://example.com/posts/1/en": {
       "body": "World commodities were up today with heavy trading of crude oil...",
       "words": 1539
     },
     "http://example.com/posts/1/de": {
       "body": "Die Werte an Warenbörsen stiegen im Sog eines starken Handels von Rohöl...",
       "words": 1204
     }
  }
}

In this case, the post property indexes two node objects via their @id. When expanded, it becomes the following:

[
  {
    "@id": "http://example.com/",
    "@type": ["http://schema.org/Blog"],
    "http://schema.org/blogPost": [{
      "@id": "http://example.com/posts/1/de",
      "http://schema.org/articleBody": [
        {"@value": "Die Werte an Warenbörsen stiegen im Sog eines starken Handels von Rohöl..."}
      ],
      "http://schema.org/wordCount": [{"@value": 1204}]
    }, {
      "@id": "http://example.com/posts/1/en",
      "http://schema.org/articleBody": [
        {"@value": "World commodities were up today with heavy trading of crude oil..."}
      ],
      "http://schema.org/wordCount": [{"@value": 1539}]
    }],
    "http://schema.org/name": [{"@value": "World Financial News"}]
  }
]

As with Language Indexing and Data Indexing, node objects without an @id can be indexed using @none.

Type Indexing

This uses the same principle as Identifier Indexing, but using the first value of @type instead.

{
  "@context": {
    "@version": 1.1,
    "schema": "http://schema.org/",
    "name": "schema:name",
    "affiliation": {
      "@id": "schema:affiliation",
      "@container": "@type"
    }
  },
  "name": "Manu Sporny",
  "affiliation": {
    "schema:Corporation": {
      "@id": "https://digitalbazaar.com/",
      "name": "Digital Bazaar"
    },
    "schema:ProfessionalService": {
      "@id": "https://spec-ops.io",
      "name": "Spec-Ops"
    }
  }
}

Named Graph Indexing

Named Graphs have been supported since 1.0 using the @graph key, but this adds semantic overhead to the representation, so JSON-LD 1.1 supports property values which are interpreted as anonymous named graphs as well:

{
  "@context": {
    "@version": 1.1,
    "generatedAt": {
      "@id": "http://www.w3.org/ns/prov#generatedAtTime",
      "@type": "http://www.w3.org/2001/XMLSchema#date"
    },
    "Person": "http://xmlns.com/foaf/0.1/Person",
    "name": "http://xmlns.com/foaf/0.1/name",
    "knows": "http://xmlns.com/foaf/0.1/knows",
    "claim": {
      "@id": "https://w3id.org/credentials#claim",
      "@container": "@graph"
    }
  },
  "generatedAt": "2012-04-09",
  "claim": [
    {
      "@id": "http://manu.sporny.org/about#manu",
      "@type": "Person",
      "name": "Manu Sporny",
      "knows": "http://greggkellogg.net/foaf#me"
    }, {
      "@id": "http://greggkellogg.net/foaf#me",
      "@type": "Person",
      "name": "Gregg Kellogg",
      "knows": "http://manu.sporny.org/about#manu"
    }
  ]
}

In this case, because the term definition for claim has @container: @graph, the value becomes an anonymous graph. When expanded, it looks like the following:

[
  {
    "http://www.w3.org/ns/prov#generatedAtTime": [
      {
        "@value": "2012-04-09",
        "@type": "http://www.w3.org/2001/XMLSchema#date"
      }
    ],
    "https://w3id.org/credentials#claim": [
      {
        "@graph": [
          {
            "@id": "http://manu.sporny.org/about#manu",
            "@type": [
              "http://xmlns.com/foaf/0.1/Person"
            ],
            "http://xmlns.com/foaf/0.1/knows": [
              {
                "@value": "http://greggkellogg.net/foaf#me"
              }
            ],
            "http://xmlns.com/foaf/0.1/name": [
              {
                "@value": "Manu Sporny"
              }
            ]
          },
          {
            "@id": "http://greggkellogg.net/foaf#me",
            "@type": [
              "http://xmlns.com/foaf/0.1/Person"
            ],
            "http://xmlns.com/foaf/0.1/knows": [
              {
                "@value": "http://manu.sporny.org/about#manu"
              }
            ],
            "http://xmlns.com/foaf/0.1/name": [
              {
                "@value": "Gregg Kellogg"
              }
            ]
          }
        ]
      }
    ]
  }
]

There are variations to allow graphs to be indexed via their explicit identifier, or an @index value as well.

Values as arrays

Since JSON-LD 1.0, it has been possible to ensure that singular property values were represented as an array using the @container: @set term definition; this is useful to simplify access code which would otherwise need to check to see if the value is singular, or already in an array form, due to the presence of multiple values. However, if the @container was set to something else, such as @language or @index, there was no way to also use @set. JSON-LD allows the @container property in a term definition to have an array form, so that @set can be specified as well as other values.

{
  "@context": {
    "vocab": "http://example.com/vocab/",
    "label": {
      "@id": "vocab:label",
      "@container": ["@language", "@set"]
    }
  },
  "@id": "http://example.com/queen",
  "label": {
    "en": ["The Queen"],
    "de": [ "Die Königin", "Ihre Majestät" ]
  }
}

When compacting data, it will always use an array form.

Scoped Contexts

JSON-LD uses a context to give value to data, and the @context key could appear anywhere in a document to layer on a new interpretation for terms. But in some cases, such as when framing, it's not possible to specify an embedded context. JSON-LD 1.1 introduces the scoped context, which allows a context to be specified as part of a term definition (which may recursively define terms with scoped contexts). This can be used in two ways, the first is for values of a property defined in such a way:

{
  "@context": {
    "@version": 1.1,
    "@vocab": "@http://schema.org/",
    "name": "http://schema.org/name",
    "interest": {
      "@id": "http://xmlns.com/foaf/0.1/interest",
      "@context": {"@vocab": "http://xmlns.com/foaf/0.1/"}
    }
  },
  "name": "Manu Sporny",
  "interest": {
    "@id": "https://www.w3.org/TR/json-ld/",
    "name": "JSON-LD",
    "topic": "Linking Data"
  }
}

In this case, the top-level context creates a common vocabulary at schema.org with some term definitions. For values of interest, the embedded @context is added, which changes the default vocabulary to FOAF.

Scoping can also be performed based on @type:

  "@context": {
    "@version": 1.1,
    "name": "http://schema.org/name",
    "interest": "http://xmlns.com/foaf/0.1/interest",
    "Document": {
      "@id": "http://xmlns.com/foaf/0.1/Document",
      "@context": {"@vocab": "http://xmlns.com/foaf/0.1/"}
    }
  },
  "@type": "Person",
  "name": "Manu Sporny",
  "interest": {
    "@id": "https://www.w3.org/TR/json-ld/",
    "@type": "Document",
    "name": "JSON-LD",
    "topic": "Linking Data"
  }
}

In this case, the fact that the @type of is Document causes the scoped context to be applied.

Transparent Nesting

Many JSON APIs separate properties from their entities using an intermediate object; in JSON-LD these are called nested properties. For example, a set of possible labels may be grouped under a common property:

{
  "@context": {
    "@version": 1.1,
    "skos": "http://www.w3.org/2004/02/skos/core#",
    "labels": "@nest",
    "main_label": {"@id": "skos:prefLabel"},
    "other_label": {"@id": "skos:altLabel"},
    "homepage": {"@id": "http://schema.org/description", "@type": "@id"}
  },
  "@id": "http://example.org/myresource",
  "homepage": "http://example.org",
  "labels": {
     "main_label": "This is the main label for my resource",
     "other_label": "This is the other label"
  }
}

In this case, the values of labels are treated as if they were made at the top level. Nesting can be recursive as well.

Similarly, node definitions may contain a @nest property to reference a term aliased to @nest which causes such values to be nested under that aliased term when compacting.

{
  "@context": {
    "@version": 1.1,
    "skos": "http://www.w3.org/2004/02/skos/core#",
    "labels": "@nest",
    "main_label": {"@id": "skos:prefLabel", "@nest": "labels"},
    "other_label": {"@id": "skos:altLabel", "@nest": "labels"},
    "homepage": {"@id": "http://schema.org/description", "@type": "@id"}
  },
  "@id": "http://example.org/myresource",
  "homepage": "http://example.org",
  "labels": {
     "main_label": "This is the main label for my resource",
     "other_label": "This is the other label"
  }
}