a simplified Turtle-like profile for JSON-LD

i expected this to have come up already, but i didn't find this sentiment in
list archives (including the CG and WG lists) or GitHub issues. i'm sure i'm
not the only one to have thought this, though.

in the interest of brevity (and foreshadowing), i'll leave most of this as
bullets instead of prose.

* the full JSON-LD spec is large and complex, and isn't really "lightweight" (anymore)
* common implementations are huge (jsonld.js is 1.2 MB unminified, 287 KB minified, 84 KB min+gzip)
* "developer friendly idiomatic JSON":
  - counterproductive for pure LD applications
  - coercions and other mappings mean a casual human reader can't actually be sure what the document is saying (as RDF)
  - need to know the JSON schema (formal or informal) of every document type one expects to encounter
  - assumes it's too hard, or too much code, to parse and work with RDF directly
  - relative URIs are desirable; those still need to be resolved
* external contexts are problematic:
  - privacy
  - complexity
  - availability, especially when disconnected
  - temporal decoupling
* i'd use Turtle for pure Linked Data applications, but processing Turtle is a lot of code and complexity too, unfortunately

i propose a simplified profile of JSON-LD, inspired by Turtle:

* @context can only define an @base, @vocab, and compact IRI prefixes
* IRIs are always in an @id member (except for @type)
* one graph per document (no @graph, use @included if needed)

* no external contexts (documents are self-contained)
* no recursive compact IRI prefix definitions
* no coercions (except for @type itself) or other transformations
* no @index, @reverse, @nest, @protected, @propogate, @import, etc.

i'm calling this the "Terse" profile (since Turtle is the "Terse RDF Triple
Language"), identified (for now) by profile URI

    http://zenomt.com/ns/jsonld-terse

dereferencing that URI yields a more detailed and precise description of the
constraints.

my reference implementation for this profile is at

    https://github.com/zenomt/jsonld-terse

it's currently 201 lines of plain JavaScript (about 7 KB, GitHub says "170 loc")
with no external dependencies, and gzips to 2116 bytes. it parses and expands
conforming documents to interconnected Plain Old JavaScript Objects, resolves
relative URIs given the document's URI and any @base directives, and can
render a graph as a JSON-LD tree rooted from any node (optionally relativizing
URIs to a provided base, but not "compacting" with an @context) or as an array
of triples. there's a test page included in the repo, and it's published to
GitHub Pages so there's a live test page to play with.

limitations of this implementation: @list is intentionally not expanded to
an RDF List, since RDF Lists are harder to work with than JavaScript Arrays;
only URIs are processed, since web browser JavaScript doesn't provide native
IRI processing functions. IRIs in documents might end up transformed to URIs
according to the JavaScript `URL` API.

example Terse JSON-LD document:
```
{
    "@context": {
        "@base":  "https://example.com/people/card",
        "foaf":   "http://xmlns.com/foaf/0.1/",
        "schema": "http://schema.org/"
    },
    "@id": "#me",
    "@type": ["foaf:Person", "schema:Person"],
    "foaf:name": { "@value": "Michael Thornburgh", "@language": "en-us" },
    "foaf:nick": "Mike",
    "foaf:depiction": { "@id": "mike.jpg" },
    "schema:worksFor": {
        "@type": "schema:Corporation",
        "schema:name": "Example Corp.",
        "schema:employee": { "@id": "#me" }
    },
    "@included": [
        { "@id": "", "@type": "foaf:PersonalProfileDocument", "foaf:primaryTopic": { "@id": "#me" } },
        { "@id": "_:b86", "foaf:knows": { "@id": "_:b99" } },
        { "@id": "_:b99", "foaf:knows": { "@id": "_:b86" } }
    ]
}
```

equivalently expressed as idiomatic Turtle:
```
@base           <https://example.com/people/card> .
@prefix foaf:   <http://xmlns.com/foaf/0.1/> .
@prefix schema: <http://schema.org/> .

<#me>
    a foaf:Person, schema:Person;
    foaf:name "Michael Thornburgh"@en-us;
    foaf:nick "Mike";
    foaf:depiction <mike.jpg>;
    schema:worksFor [
        a schema:Corporation;
        schema:name "Example Corp.";
        schema:employee <#me>
    ] .

<> a foaf:PersonalProfileDocument; foaf:primaryTopic <#me> .

_:b86 foaf:knows _:b99 .
_:b99 foaf:knows _:b86 .
```

i hope this is of interest and use to others.

-michael thornburgh

Received on Sunday, 31 March 2024 10:18:08 UTC