JSON LD vs RDF/JSON (Alternative serialization)

I’d like to say that I am rarely opinionated about technology but that would be a barefaced lie. It is true, however, that I am rarely so opinionated that I motivate myself to write about it. This, unfortunately, is one of those occasions.

On January the 16th the JSON-LD specification became a W3C recommendation. There was an “Alternative Serialization” of RDF into JSON, referred to as RDF/JSON. This format is not to become a recommendation:

The RDF Working Group has decided not to push this document through the W3C Recommendation Track. [from http://www.w3.org/TR/rdf-json/]

I think that decision was a mistake; particularly from the perspective of an RDF user. I have no real right to complain as I was not involved with the working group or a member of the mailing list; I can whine now, though.

Differences

In a nutshell:

RDF/JSON (the Alternative Syntax) is very simple. It maps obviously to Notation 3 and the RDF core concept.

JSON-LD is not specific to RDF and it has lots of features to make it more readable to a human, which makes it more verbose.

Using the syntaxes

I am making an assumption that, in choosing JSON as a format, the user of one of the two syntaxes would likely be a JavaScript developer. So, I put together a minimal example of processing RDF written in both syntaxes. In both cases I have mapped the following Turtle into JSON in the tersest way I could – specifically, I did not use the JSON-LD “@context” attribute, in order to give JSON-LD the fairest chance. Then, for each JSON object I wrote a simple JavaScript program to convert the RDF back into Turtle. Here are the two programs for comparison:

Using the “Alternative Syntax”:

function printLiteralTriple(subject, predicate, value, language) {
  document.write("<" + subject + "> <" + predicate + "> "" + value + ""@" + language + " .");
  document.write("<br/>");
}

var jsonAlt = {
  "http://example.org/about" : {
      "http://purl.org/dc/terms/title" : [ { "value" : "Anna's Homepage", 
                                             "type" : "literal", 
                                             "lang" : "en" } ] 
  }
}

for (var subject in jsonAlt) {
  for (var predicate in jsonAlt[subject]) {
    for (var objectIndex in jsonAlt[subject][predicate]) {
      var object = jsonAlt[subject][predicate][objectIndex];
      printLiteralTriple(subject, predicate, object.value, object.lang);
    }
  }
}

See the Pen zoFHC by chrismichaelscott (@chrismichaelscott) on CodePen.

Note that the iteration is east to understand as the keys hold useful content – a common construct in JavaScript – and are homogeneous (i.e. the top level keys are always subjects).

Using JSON-LD:

function printLiteralTriple(subject, predicate, value, language) {
  document.write("&lt;" + subject + "&gt; &lt;" + predicate + "&gt; "" + value + ""@" + language + " .");
  document.write("<br/>");
}

var jsonLD = {
  "@id": "http://example.org/about",
  "http://purl.org/dc/terms/title": {
    "@value": "Anna's Homepage",
    "@type": "literal", 
    "@language" : "en"
  }
}

for (var key in jsonLD) {
  var subject;

  if (key == "@id") {
    subject = jsonLD[key];
  } else { // NOTE: there are other cases to check for here, such as @context
    var predicate = key;
    var object = jsonLD[key];

    if (object instanceof Array) {
      for (var objectIndex in object) {
        printLiteralTriple(subject, predicate, object[objectIndex]["@value"], object[objectIndex]["@language"]);
      }
    } else {
      printLiteralTriple(subject, predicate, object["@value"], object["@language"]);
    }
  }
}

See the Pen JaeIG by chrismichaelscott (@chrismichaelscott) on CodePen.

Because the keys in JSON-LD are not homogeneous (i.e. they may be keywords, like “@id”, or they may be predicates) the developer cannot simply iterate over the object.

Computer readable verses human readable

A fully featured JSON-LD document may be more complex than the example above. One can use the “@context” key to provide typing information and prefixes (as they would be in Turtle). That feature can arguably make the document easier for a human to read but is that really the objective? Look what happens to the JavaScript parser when we include a “@context” – and consider that this is a minimal parser that doesn’t support any case other than a literal valued triple.

var jsonLD = {
  "@context": {
    "title": {
      "@id": "http://purl.org/dc/terms/title",
      "@type": "@value",
      "@language": "en"
    }
  },
  "@id": "http://example.org/about",
  "title": "Anna's Homepage"
}

function printLiteralTriple(subject, predicate, value, language) {
  document.write("&lt;" + subject + "&gt; &lt;" + predicate + "&gt; "" + value + ""@" + language + " .");
  document.write("<br/>");
}

var contexts = {};

// first resolve contexts
for (var key in jsonLD) {
  if (key == "@context") {
    if (jsonLD["@context"] instanceof Array) {
      for (var contextIndex in jsonLD["@context"]) {
        for (var context in jsonLD["@context"][contextIndex]) {
          contexts[context] = jsonLD["@context"][contextIndex][context];
        }
      }
    } else {
      for (var context in jsonLD["@context"]) {
        contexts[context] = jsonLD["@context"][context];
      }
    }
  }
}

for (var key in jsonLD) {
  var subject;

  if (key == "@id") {
    subject = jsonLD[key];
  } else if (key != "@context") {
    var predicate = key;
    var object = jsonLD[key];
    var objectLanguage;

    if (typeof(contexts[predicate]) !== "undefined") {
      if (typeof(contexts[predicate]["@language"]) !== "undefined") {
        objectLanguage = contexts[predicate]["@language"];
      }
      predicate = contexts[predicate]["@id"];
    }

    if (object instanceof Array) {
      for (var objectIndex in object) {
        var objectValue;
        if (typeof(object[objectIndex]) == "string") {
          objectValue = object[objectIndex];
        } else {
          if (typeof(objectLanguage) == "undefined") {
            objectLanguage = object[objectIndex]["@language"];
          }
          objectValue = object[objectIndex]["@value"];
        }
        printLiteralTriple(subject, predicate, objectValue, objectLanguage);
      }
    } else {
      var objectValue;
      if (typeof(object) == "string") {
        objectValue = object;
      } else {
        if (typeof(objectLanguage) == "undefined") {
          objectLanguage = object["@language"];
        }
        objectValue = object["@value"];
      }
      printLiteralTriple(subject, predicate, objectValue, objectLanguage);
    }
  }
}

See the Pen jiarn by chrismichaelscott (@chrismichaelscott) on CodePen.

Not great, is it? I really hope the working group reconsider. For human readable RDF use Turtle: it’s great at that. For RDF to be consumed by object oriented languages, use RDF/JSON, the “Alternative Serialization”. 🙁

Leave a Reply

Your email address will not be published. Required fields are marked *