Update playground to use new JSON-LD syntax.
authorDave Longley <dlongley@digitalbazaar.com>
Tue, 20 Dec 2011 00:07:26 -0500
changeset 299 a0b3b274ed7a
parent 298 4e36cba4fc71
child 300 48d1c2f9bbbf
Update playground to use new JSON-LD syntax.
playground/jsonld-turtle.js
playground/jsonld.js
playground/lang-jsonld.js
playground/playground-examples.js
--- a/playground/jsonld-turtle.js	Thu Dec 15 16:55:21 2011 -0500
+++ b/playground/jsonld-turtle.js	Tue Dec 20 00:07:26 2011 -0500
@@ -10,7 +10,7 @@
 
 /**
  * Retrieves all of the properties that are a part of a JSON-LD object, 
- * ignoring the "@subject" key.
+ * ignoring the "@id" key.
  *
  * @param obj the JSON-LD object - the last part of the triple.
  *
@@ -23,7 +23,7 @@
    // accumulate the names of all non-JSON-LD subjects
    for(var key in obj)
    {
-      if(key != "@subject")
+      if(key != "@id")
       {
          rval.push(key);
       }
@@ -105,17 +105,17 @@
    else if(obj instanceof Object)
    {
       // the object is an IRI, typed literal or language-tagged literal
-      if("@literal" in obj && "@datatype" in obj)
+      if("@literal" in obj && "@type" in obj)
       {
-         rval = "\"" + obj["@literal"] + "\"^^<" + obj["@datatype"] + ">";
+         rval = "\"" + obj["@literal"] + "\"^^<" + obj["@type"] + ">";
       }
       else if("@literal" in obj && "@language" in obj)
       {
          rval = "\"" + obj["@literal"] + "\"@" + obj["@language"];
       }
-      else if("@iri" in obj)
+      else if("@id" in obj)
       {
-         var iri = obj["@iri"];
+         var iri = obj["@id"];
          rval = iriToTurtle(iri);
       }
    }
@@ -144,7 +144,13 @@
    {
       // print out each key in the normalized array (the subjects)
       var subject = normalized[s];
-      var iri = subject["@subject"]["@iri"];
+      var iri = subject["@id"];
+      
+      // skip subjects with no properties (no triples to generate)
+      if(Object.keys(subject).length === 1)
+      {
+         continue;
+      }
 
       rval += iriToTurtle(iri) + "\n";
 
--- a/playground/jsonld.js	Thu Dec 15 16:55:21 2011 -0500
+++ b/playground/jsonld.js	Tue Dec 20 00:07:26 2011 -0500
@@ -25,6 +25,27 @@
    Exception = function(obj)
    {
       _setMembers(this, obj);
+   };
+
+   // define js 1.8.5 Object.keys method unless present
+   if(!Object.keys)
+   {
+      Object.keys = function(o)
+      {  
+         if(o !== Object(o))
+         {
+            throw new TypeError('Object.keys called on non-object');
+         }
+         var rval = [];
+         for(var p in o)
+         {
+            if(Object.prototype.hasOwnProperty.call(o, p))
+            {
+               rval.push(p);
+            }
+         }
+         return rval;
+      }
    }
 }
 // define node.js module
@@ -49,9 +70,9 @@
 
 var xsd =
 {
-   boolean: ns.xsd + 'boolean',
-   double: ns.xsd + 'double',
-   integer: ns.xsd + 'integer'
+   'boolean': ns.xsd + 'boolean',
+   'double': ns.xsd + 'double',
+   'integer': ns.xsd + 'integer'
 };
 
 /**
@@ -133,11 +154,9 @@
    
    var rval =
    {
-      '@datatype': '@datatype',
-      '@iri': '@iri',
+      '@id': '@id',
       '@language': '@language',
       '@literal': '@literal',
-      '@subject': '@subject',
       '@type': '@type'
    };
    
@@ -147,8 +166,7 @@
       var keywords = {};
       for(var key in ctx)
       {
-         if(ctx[key].constructor === String &&
-            ctx[key] in rval)
+         if(ctx[key].constructor === String && ctx[key] in rval)
          {
             keywords[ctx[key]] = key;
          }
@@ -165,7 +183,32 @@
 };
 
 /**
- * Compacts an IRI into a term or CURIE if it can be. IRIs will not be
+ * Gets the iri associated with a term.
+ * 
+ * @param ctx the context.
+ * @param term the term.
+ * 
+ * @return the iri or NULL.
+ */
+var _getTermIri = function(ctx, term)
+{
+   var rval = null;
+   if(term in ctx)
+   {
+      if(ctx[term].constructor === String)
+      {
+         rval = ctx[term];
+      }
+      else if(ctx[term].constructor === Object && '@id' in ctx[term])
+      {
+         rval = ctx[term]['@id'];
+      }
+   }
+   return rval;
+};
+
+/**
+ * Compacts an IRI into a term or prefix if it can be. IRIs will not be
  * compacted to relative IRIs if they match the given context's default
  * vocabulary.
  *
@@ -173,26 +216,26 @@
  * @param iri the IRI to compact.
  * @param usedCtx a context to update if a value was used from "ctx".
  *
- * @return the compacted IRI as a term or CURIE or the original IRI.
+ * @return the compacted IRI as a term or prefix or the original IRI.
  */
 var _compactIri = function(ctx, iri, usedCtx)
 {
    var rval = null;
    
    // check the context for a term that could shorten the IRI
-   // (give preference to terms over CURIEs)
+   // (give preference to terms over prefixes)
    for(var key in ctx)
    {
       // skip special context keys (start with '@')
       if(key.length > 0 && key[0] !== '@')
       {
          // compact to a term
-         if(iri === ctx[key])
+         if(iri === _getTermIri(ctx, key))
          {
             rval = key;
             if(usedCtx !== null)
             {
-               usedCtx[key] = ctx[key];
+               usedCtx[key] = _clone(ctx[key]);
             }
             break;
          }
@@ -205,7 +248,7 @@
       rval = _getKeywords(ctx)['@type'];
    }
    
-   // term not found, check the context for a CURIE prefix
+   // term not found, check the context for a prefix
    if(rval === null)
    {
       for(var key in ctx)
@@ -214,18 +257,21 @@
          if(key.length > 0 && key[0] !== '@')
          {
             // see if IRI begins with the next IRI from the context
-            var ctxIri = ctx[key];
-            var idx = iri.indexOf(ctxIri);
-            
-            // compact to a CURIE
-            if(idx === 0 && iri.length > ctxIri.length)
+            var ctxIri = _getTermIri(ctx, key);
+            if(ctxIri !== null)
             {
-               rval = key + ':' + iri.substr(ctxIri.length);
-               if(usedCtx !== null)
+               var idx = iri.indexOf(ctxIri);
+               
+               // compact to a prefix
+               if(idx === 0 && iri.length > ctxIri.length)
                {
-                  usedCtx[key] = ctxIri;
+                  rval = key + ':' + iri.substr(ctxIri.length);
+                  if(usedCtx !== null)
+                  {
+                     usedCtx[key] = _clone(ctx[key]);
+                  }
+                  break;
                }
-               break;
             }
          }
       }
@@ -242,7 +288,7 @@
 
 /**
  * Expands a term into an absolute IRI. The term may be a regular term, a
- * CURIE, a relative IRI, or an absolute IRI. In any case, the associated
+ * prefix, a relative IRI, or an absolute IRI. In any case, the associated
  * absolute IRI will be returned.
  *
  * @param ctx the context to use.
@@ -253,67 +299,52 @@
  */
 var _expandTerm = function(ctx, term, usedCtx)
 {
-   var rval;
+   var rval = term;
    
    // get JSON-LD keywords
    var keywords = _getKeywords(ctx);
    
-   // 1. If the property has a colon, then it is a CURIE or an absolute IRI:
+   // 1. If the property has a colon, it is a prefix or an absolute IRI:
    var idx = term.indexOf(':');
-   if(idx != -1)
+   if(idx !== -1)
    {
-      // get the potential CURIE prefix
+      // get the potential prefix
       var prefix = term.substr(0, idx);
 
-      // 1.1. See if the prefix is in the context:
+      // expand term if prefix is in context, otherwise leave it be
       if(prefix in ctx)
       {
          // prefix found, expand property to absolute IRI
-         rval = ctx[prefix] + term.substr(idx + 1);
+         var iri = _getTermIri(ctx, prefix);
+         rval = iri + term.substr(idx + 1);
          if(usedCtx !== null)
          {
-            usedCtx[prefix] = ctx[prefix];
+            usedCtx[prefix] = _clone(ctx[prefix]);
          }
       }
-      // 1.2. Prefix is not in context, property is already an absolute IRI:
-      else
-      {
-         rval = term;
-      }
    }
    // 2. If the property is in the context, then it's a term.
    else if(term in ctx)
    {
-      rval = ctx[term];
+      rval = _getTermIri(ctx, term);
       if(usedCtx !== null)
       {
-         usedCtx[term] = rval;
+         usedCtx[term] = _clone(ctx[term]);
       }
    }
-   // 3. The property is the special-case @subject.
-   else if(term === keywords['@subject'])
-   {
-      rval = '@subject';
-   }
-   // 4. The property is the special-case @type.
-   else if(term === keywords['@type'])
-   {
-      rval = '@type';
-   }
-   // 5. The property is a relative IRI, prepend the default vocab.
+   // 3. The property is a keyword.
    else
    {
-      rval = term;
-      if('@vocab' in ctx)
+      for(var key in keywords)
       {
-         rval = ctx['@vocab'] + rval;
-         if(usedCtx !== null)
+         if(term === keywords[key])
          {
-            usedCtx['@vocab'] = ctx['@vocab'];
+            rval = key;
+            break;
          }
       }
    }
-
+   
    return rval;
 };
 
@@ -326,27 +357,56 @@
  */
 var _sortContextKeys = function(ctx)
 {
+   // sort keys
    var rval = {};
-   
-   // sort keys
    var keys = Object.keys(ctx).sort();
    for(var k in keys)
    {
       var key = keys[k];
-      if(key !== '@coerce')
-      {
-         rval[key] = ctx[key];
-      }
+      rval[key] = ctx[key];
    }
-   if('@coerce' in ctx)
+   return rval;
+};
+
+/**
+ * Gets whether or not a value is a reference to a subject (or a subject with
+ * no properties).
+ * 
+ * @param value the value to check.
+ * 
+ * @return true if the value is a reference to a subject, false if not.
+ */
+var _isReference = function(value)
+{
+   // Note: A value is a reference to a subject if all of these hold true:
+   // 1. It is an Object.
+   // 2. It is has an @id key.
+   // 3. It has only 1 key.
+   return (value !== null &&
+      value.constructor === Object &&
+      '@id' in value &&
+      Object.keys(value).length === 1);
+};
+
+/**
+ * Gets whether or not a value is a subject with properties.
+ * 
+ * @param value the value to check.
+ * 
+ * @return true if the value is a subject with properties, false if not.
+ */
+var _isSubject = function(value)
+{
+   var rval = false;
+   
+   // Note: A value is a subject if all of these hold true:
+   // 1. It is an Object.
+   // 2. It is not a literal.
+   // 3. It has more than 1 key OR any existing key is not '@id'.
+   if(value !== null && value.constructor === Object && !('@literal' in value))
    {
-      rval['@coerce'] = {};
-      keys = Object.keys(ctx['@coerce']).sort();
-      for(var k in keys)
-      {
-         var key = keys[k];
-         rval['@coerce'][key] = ctx['@coerce'][key];
-      }
+      var keyCount = Object.keys(value).length;
+      rval = (keyCount > 1 || !('@id' in value));
    }
    
    return rval;
@@ -377,7 +437,7 @@
  */
 jsonld.expand = function(input)
 {
-   return new Processor().expand({}, null, input, false);
+   return new Processor().expand({}, null, input);
 };
 
 /**
@@ -504,7 +564,7 @@
             {
                if(merged[mkey] === ctx2[key])
                {
-                  // FIXME: update related @coerce rules
+                  // FIXME: update related coerce rules
                   delete merged[mkey];
                   break;
                }
@@ -515,27 +575,7 @@
       // merge contexts
       for(var key in ctx2)
       {
-         // skip @coerce, to be merged below
-         if(key !== '@coerce')
-         {
-            merged[key] = _clone(ctx2[key]);
-         }
-      }
-      
-      // merge @coerce
-      if('@coerce' in ctx2)
-      {
-         if(!('@coerce' in merged))
-         {
-            merged['@coerce'] = _clone(ctx2['@coerce']);
-         }
-         else
-         {
-            for(var key in ctx2['@coerce'])
-            {
-               merged['@coerce'][key] = ctx2['@coerce'][key];
-            }
-         }
+         merged[key] = _clone(ctx2[key]);
       }
    }
 
@@ -544,7 +584,7 @@
 
 /**
  * Expands a term into an absolute IRI. The term may be a regular term, a
- * CURIE, a relative IRI, or an absolute IRI. In any case, the associated
+ * prefix, a relative IRI, or an absolute IRI. In any case, the associated
  * absolute IRI will be returned.
  *
  * @param ctx the context to use.
@@ -555,14 +595,14 @@
 jsonld.expandTerm = _expandTerm;
 
 /**
- * Compacts an IRI into a term or CURIE if it can be. IRIs will not be
+ * Compacts an IRI into a term or prefix if it can be. IRIs will not be
  * compacted to relative IRIs if they match the given context's default
  * vocabulary.
  *
  * @param ctx the context to use.
  * @param iri the IRI to compact.
  *
- * @return the compacted IRI as a term or CURIE or the original IRI.
+ * @return the compacted IRI as a term or prefix or the original IRI.
  */
 jsonld.compactIri = function(ctx, iri)
 {
@@ -603,7 +643,7 @@
    var rval = null;
    
    // normalize input
-   normalized = jsonld.normalize(input);
+   var normalized = jsonld.normalize(input);
    
    // setup default callback
    callback = callback || null;
@@ -621,10 +661,10 @@
    for(var i1 in normalized)
    {
       var e = normalized[i1];
-      var s = e['@subject']['@iri'];
+      var s = e['@id'];
       for(var p in e)
       {
-         if(p !== '@subject')
+         if(p !== '@id')
          {
             var obj = e[p];
             if(obj.constructor !== Array)
@@ -791,7 +831,7 @@
 };
 
 /**
- * Recursively compacts a value. This method will compact IRIs to CURIEs or
+ * Recursively compacts a value. This method will compact IRIs to prefixes or
  * terms and do reverse type coercion to compact a value.
  *
  * @param ctx the context to use.
@@ -826,16 +866,14 @@
    // graph literal/disjoint graph
    else if(
       value.constructor === Object &&
-      '@subject' in value && value['@subject'].constructor === Array)
+      '@id' in value && value['@id'].constructor === Array)
    {
       rval = {};
-      rval[keywords['@subject']] = this.compact(
-         ctx, property, value['@subject'], usedCtx);
+      rval[keywords['@id']] = this.compact(
+         ctx, property, value['@id'], usedCtx);
    }
-   // value has sub-properties if it doesn't define a literal or IRI value
-   else if(
-      value.constructor === Object &&
-      !('@literal' in value) && !('@iri' in value))
+   // recurse if value is a subject
+   else if(_isSubject(value))
    {
       // recursively handle sub-properties that aren't a sub-context
       rval = {};
@@ -849,7 +887,7 @@
             if(p !== key || !(p in rval))
             {
                // FIXME: clean old values from the usedCtx here ... or just
-               // change usedCtx to be built at the end of processing? 
+               // change usedCtx to be built at the end of processing?
                rval[p] = this.compact(ctx, key, value[key], usedCtx);
             }
          }
@@ -867,15 +905,15 @@
          // type coercion can only occur if language is not specified
          if(!('@language' in value))
          {
-            // datatype must match coerce type if specified
-            if('@datatype' in value)
+            // type must match coerce type if specified
+            if('@type' in value)
             {
-               type = value['@datatype'];
+               type = value['@type'];
             }
-            // datatype is IRI
-            else if('@iri' in value)
+            // type is ID (IRI)
+            else if('@id' in value)
             {
-               type = '@iri';
+               type = '@id';
             }
             // can be coerced to any type
             else
@@ -892,7 +930,8 @@
 
       // types that can be auto-coerced from a JSON-builtin
       if(coerce === null &&
-         (type === xsd.boolean || type === xsd.integer || type === xsd.double))
+         (type === xsd['boolean'] || type === xsd['integer'] ||
+         type === xsd['double']))
       {
          coerce = type;
       }
@@ -913,7 +952,7 @@
          else if(type !== coerce)
          {
             throw new Exception({
-               message: 'Cannot coerce type because the datatype does ' +
+               message: 'Cannot coerce type because the type does ' +
                   'not match.',
                type: type,
                expected: coerce
@@ -924,9 +963,9 @@
          {
             if(value.constructor === Object)
             {
-               if('@iri' in value)
+               if('@id' in value)
                {
-                  rval = value['@iri'];
+                  rval = value['@id'];
                }
                else if('@literal' in value)
                {
@@ -939,15 +978,15 @@
             }
 
             // do basic JSON types conversion
-            if(coerce === xsd.boolean)
+            if(coerce === xsd['boolean'])
             {
                rval = (rval === 'true' || rval != 0);
             }
-            else if(coerce === xsd.double)
+            else if(coerce === xsd['double'])
             {
                rval = parseFloat(rval);
             }
-            else if(coerce === xsd.integer)
+            else if(coerce === xsd['integer'])
             {
                rval = parseInt(rval);
             }
@@ -968,12 +1007,12 @@
       }
 
       // compact IRI
-      if(type === '@iri')
+      if(type === '@id')
       {
          if(rval.constructor === Object)
          {
-            rval[keywords['@iri']] = _compactIri(
-               ctx, rval[keywords['@iri']], usedCtx);
+            rval[keywords['@id']] = _compactIri(
+               ctx, rval[keywords['@id']], usedCtx);
          }
          else
          {
@@ -992,11 +1031,10 @@
  * @param ctx the context.
  * @param property the property that points to the value, NULL for none.
  * @param value the value to expand.
- * @param expandSubjects true to expand subjects (normalize), false not to.
  *
  * @return the expanded value.
  */
-Processor.prototype.expand = function(ctx, property, value, expandSubjects)
+Processor.prototype.expand = function(ctx, property, value)
 {
    var rval;
    
@@ -1019,7 +1057,7 @@
       rval = [];
       for(var i in value)
       {
-         rval.push(this.expand(ctx, property, value[i], expandSubjects));
+         rval.push(this.expand(ctx, property, value[i]));
       }
    }
    else if(value.constructor === Object)
@@ -1030,50 +1068,22 @@
          ctx = jsonld.mergeContexts(ctx, value['@context']);
       }
       
-      // get JSON-LD keywords
-      var keywords = _getKeywords(ctx);
-      
-      // value has sub-properties if it doesn't define a literal or IRI value
-      if(!(keywords['@literal'] in value || keywords['@iri'] in value))
+      // recursively handle sub-properties that aren't a sub-context
+      rval = {};
+      for(var key in value)
       {
-         // recursively handle sub-properties that aren't a sub-context
-         rval = {};
-         for(var key in value)
+         // preserve frame keywords
+         if(key === '@embed' || key === '@explicit' ||
+            key === '@default' || key === '@omitDefault')
          {
-            // preserve frame keywords
-            if(key === '@embed' || key === '@explicit' ||
-               key === '@default' || key === '@omitDefault')
-            {
-               _setProperty(rval, key, _clone(value[key]));
-            }
-            else if(key !== '@context')
-            {
-               // set object to expanded property
-               _setProperty(
-                  rval, _expandTerm(ctx, key, null),
-                  this.expand(ctx, key, value[key], expandSubjects));
-            }
+            _setProperty(rval, key, _clone(value[key]));
          }
-      }
-      // only need to expand keywords
-      else
-      {
-         rval = {};
-         if(keywords['@iri'] in value)
+         else if(key !== '@context')
          {
-            rval['@iri'] = value[keywords['@iri']];
-         }
-         else
-         {
-            rval['@literal'] = value[keywords['@literal']];
-            if(keywords['@language'] in value)
-            {
-               rval['@language'] = value[keywords['@language']];
-            }
-            else if(keywords['@datatype'] in value)
-            {
-               rval['@datatype'] = value[keywords['@datatype']];
-            }
+            // set object to expanded property
+            _setProperty(
+               rval, _expandTerm(ctx, key, null),
+               this.expand(ctx, key, value[key]));
          }
       }
    }
@@ -1091,34 +1101,38 @@
       {
          if(value.constructor === Boolean)
          {
-            coerce = xsd.boolean;
+            coerce = xsd['boolean'];
          }
          else if(('' + value).indexOf('.') == -1)
          {
-            coerce = xsd.integer;
+            coerce = xsd['integer'];
          }
          else
          {
-            coerce = xsd.double;
+            coerce = xsd['double'];
          }
       }
-
-      // coerce to appropriate datatype, only expand subjects if requested
-      if(coerce !== null &&
-         (property !== keywords['@subject'] || expandSubjects))
+      
+      // special-case expand @id and @type (skips '@id' expansion)
+      if(property === keywords['@id'] || property === keywords['@type'])
+      {
+         rval = _expandTerm(ctx, value, null);
+      }
+      // coerce to appropriate type
+      else if(coerce !== null)
       {
          rval = {};
          
-         // expand IRI
-         if(coerce === '@iri')
+         // expand ID (IRI)
+         if(coerce === '@id')
          {
-            rval['@iri'] = _expandTerm(ctx, value, null);
+            rval['@id'] = _expandTerm(ctx, value, null);
          }
-         // other datatype
+         // other type
          else
          {
-            rval['@datatype'] = coerce;
-            if(coerce === xsd.double)
+            rval['@type'] = coerce;
+            if(coerce === xsd['double'])
             {
                // do special JSON-LD double format
                value = value.toExponential(6).replace(
@@ -1160,11 +1174,11 @@
       };
       
       // expand input
-      var expanded = this.expand({}, null, input, true);
+      var expanded = this.expand({}, null, input);
       
       // assign names to unnamed bnodes
       this.nameBlankNodes(expanded);
-
+      
       // flatten
       var subjects = {};
       _flatten(null, null, expanded, subjects);
@@ -1189,7 +1203,7 @@
       // sort output
       rval.sort(function(a, b)
       {
-         return _compare(a['@subject']['@iri'], b['@subject']['@iri']);
+         return _compare(a['@id'], b['@id']);
       });
    }
 
@@ -1213,27 +1227,22 @@
    var p = _expandTerm(ctx, property, null);
    
    // built-in type coercion JSON-LD-isms
-   if(p === '@subject' || p === '@type')
+   if(p === '@id' || p === '@type')
    {
-      rval = '@iri';
+      rval = '@id';
    }
-   // check type coercion for property
-   else if('@coerce' in ctx)
+   else
    {
-      // look up compacted property in coercion map
+      // look up compacted property for a coercion type
       p = _compactIri(ctx, p, null);
-      if(p in ctx['@coerce'])
+      if(p in ctx && ctx[p].constructor === Object && '@type' in ctx[p])
       {
          // property found, return expanded type
-         var type = ctx['@coerce'][p];
+         var type = ctx[p]['@type'];
          rval = _expandTerm(ctx, type, usedCtx);
          if(usedCtx !== null)
          {
-            if(!('@coerce' in usedCtx))
-            {
-               usedCtx['@coerce'] = {};
-            }
-            usedCtx['@coerce'][p] = type;
+            usedCtx[p] = _clone(ctx[p]);
          }
       }
    }
@@ -1250,17 +1259,13 @@
 {
    // look for "_:" at the beginning of the subject
    return (
-      v.constructor === Object && '@subject' in v &&
-      '@iri' in v['@subject'] && _isBlankNodeIri(v['@subject']['@iri']));
+      v.constructor === Object && '@id' in v && _isBlankNodeIri(v['@id']));
 };
 
 var _isBlankNode = function(v)
 {
-   // look for no subject or named blank node
-   return (
-      v.constructor === Object &&
-      !('@iri' in v || '@literal' in v) &&
-      (!('@subject' in v) || _isNamedBlankNode(v)));
+   // look for a subject with no ID or a blank node ID
+   return (_isSubject(v) && (!('@id' in v) || _isNamedBlankNode(v)));
 };
 
 /**
@@ -1356,16 +1361,16 @@
       {
          if('@literal' in o1)
          {
-            rval = _compareObjectKeys(o1, o2, '@datatype');
+            rval = _compareObjectKeys(o1, o2, '@type');
             if(rval === 0)
             {
                rval = _compareObjectKeys(o1, o2, '@language');
             }
          }
-         // both are '@iri' objects
+         // both are '@id' objects
          else
          {
-            rval = _compare(o1['@iri'], o2['@iri']);
+            rval = _compare(o1['@id'], o2['@id']);
          }
       }
    }
@@ -1394,59 +1399,56 @@
    3.2.3. The bnode with the alphabetically-first string is first.
    3.2.4. The bnode with a @literal is first.
    3.2.5. The bnode with the alphabetically-first @literal is first.
-   3.2.6. The bnode with the alphabetically-first @datatype is first.
+   3.2.6. The bnode with the alphabetically-first @type is first.
    3.2.7. The bnode with a @language is first.
    3.2.8. The bnode with the alphabetically-first @language is first.
-   3.2.9. The bnode with the alphabetically-first @iri is first.
+   3.2.9. The bnode with the alphabetically-first @id is first.
    */
    
    for(var p in a)
    {
-      // step #3.1
-      var lenA = (a[p].constructor === Array) ? a[p].length : 1;
-      var lenB = (b[p].constructor === Array) ? b[p].length : 1;
-      rval = _compare(lenA, lenB);
-      
-      // step #3.2.1
-      if(rval === 0)
+      // skip IDs (IRIs)
+      if(p !== '@id')
       {
-         // normalize objects to an array
-         var objsA = a[p];
-         var objsB = b[p];
-         if(objsA.constructor !== Array)
+         // step #3.1
+         var lenA = (a[p].constructor === Array) ? a[p].length : 1;
+         var lenB = (b[p].constructor === Array) ? b[p].length : 1;
+         rval = _compare(lenA, lenB);
+
+         // step #3.2.1
+         if(rval === 0)
          {
-            objsA = [objsA];
-            objsB = [objsB];
+            // normalize objects to an array
+            var objsA = a[p];
+            var objsB = b[p];
+            if(objsA.constructor !== Array)
+            {
+               objsA = [objsA];
+               objsB = [objsB];
+            }
+            
+            // compare non-bnodes (remove bnodes from comparison)
+            objsA = objsA.filter(function(e) {return !_isNamedBlankNode(e);});
+            objsB = objsB.filter(function(e) {return !_isNamedBlankNode(e);});
+            rval = _compare(objsA.length, objsB.length);
          }
          
-         // filter non-bnodes (remove bnodes from comparison)
-         objsA = objsA.filter(function(e) {
-            return (e.constructor === String ||
-               !('@iri' in e && _isBlankNodeIri(e['@iri'])));
-         });
-         objsB = objsB.filter(function(e) {
-            return (e.constructor === String ||
-               !('@iri' in e && _isBlankNodeIri(e['@iri'])));
-         });
+         // steps #3.2.2-3.2.9
+         if(rval === 0)
+         {
+            objsA.sort(_compareObjects);
+            objsB.sort(_compareObjects);
+            for(var i = 0; i < objsA.length && rval === 0; ++i)
+            {
+               rval = _compareObjects(objsA[i], objsB[i]);
+            }
+         }
          
-         rval = _compare(objsA.length, objsB.length);
-      }
-      
-      // steps #3.2.2-3.2.9
-      if(rval === 0)
-      {
-         objsA.sort(_compareObjects);
-         objsB.sort(_compareObjects);
-         for(var i = 0; i < objsA.length && rval === 0; ++i)
+         if(rval !== 0)
          {
-            rval = _compareObjects(objsA[i], objsB[i]);
+            break;
          }
       }
-      
-      if(rval !== 0)
-      {
-         break;
-      }
    }
    
    return rval;
@@ -1504,17 +1506,17 @@
    }
    else if(input.constructor === Object)
    {
-      if('@subject' in input)
+      if('@id' in input)
       {
-         // graph literal
-         if(input['@subject'].constructor == Array)
+         // graph literal/disjoint graph
+         if(input['@id'].constructor == Array)
          {
-            _collectSubjects(input['@subject'], subjects, bnodes);
+            _collectSubjects(input['@id'], subjects, bnodes);
          }
          // named subject
-         else
+         else if(_isSubject(input))
          {
-            subjects[input['@subject']['@iri']] = input;
+            subjects[input['@id']] = input;
          }
       }
       // unnamed blank node
@@ -1559,8 +1561,13 @@
    }
    else if(value.constructor === Object)
    {
+      // already-expanded value or special-case reference-only @type
+      if('@literal' in value || parentProperty === '@type')
+      {
+         flattened = _clone(value);
+      }
       // graph literal/disjoint graph
-      if('@subject' in value && value['@subject'].constructor === Array)
+      else if(value['@id'].constructor === Array)
       {
          // cannot flatten embedded graph literals
          if(parent !== null)
@@ -1571,44 +1578,36 @@
          }
          
          // top-level graph literal
-         for(var key in value['@subject'])
+         for(var idx in value['@id'])
          {
-            _flatten(parent, parentProperty, value['@subject'][key], subjects);
+            _flatten(parent, parentProperty, value['@id'][idx], subjects);
          }
       }
-      // already-expanded value
-      else if('@literal' in value || '@iri' in value)
-      {
-         flattened = _clone(value);
-      }
-      // subject
+      // regular subject
       else
       {
          // create or fetch existing subject
          var subject;
-         if(value['@subject']['@iri'] in subjects)
+         if(value['@id'] in subjects)
          {
-            // FIXME: '@subject' might be a graph literal (as {})
-            subject = subjects[value['@subject']['@iri']];
+            // FIXME: '@id' might be a graph literal (as {})
+            subject = subjects[value['@id']];
          }
          else
          {
-            subject = {};
-            if('@subject' in value)
-            {
-               // FIXME: '@subject' might be a graph literal (as {})
-               subjects[value['@subject']['@iri']] = subject;
-            }
+            // FIXME: '@id' might be a graph literal (as {})
+            subject = {'@id': value['@id']};
+            subjects[value['@id']] = subject;
          }
-         flattened = subject;
+         flattened = {'@id': subject['@id']};
 
          // flatten embeds
          for(var key in value)
          {
             var v = value[key];
             
-            // drop null values
-            if(v !== null)
+            // drop null values, skip @id (it is already set above)
+            if(v !== null && key !== '@id')
             {
                if(key in subject)
                {
@@ -1622,7 +1621,7 @@
                   subject[key] = [];
                }
                
-               _flatten(subject[key], null, v, subjects);
+               _flatten(subject[key], key, v, subjects);
                if(subject[key].length === 1)
                {
                   // convert subject[key] to object if it has only 1
@@ -1641,25 +1640,16 @@
    // add flattened value to parent
    if(flattened !== null && parent !== null)
    {
-      // remove top-level '@subject' for subjects
-      // 'http://mypredicate': {'@subject': {'@iri': 'http://mysubject'}}
-      // becomes
-      // 'http://mypredicate': {'@iri': 'http://mysubject'}
-      if(flattened.constructor === Object && '@subject' in flattened)
-      {
-         flattened = flattened['@subject'];
-      }
-
       if(parent.constructor === Array)
       {
          // do not add duplicate IRIs for the same property
          var duplicate = false;
-         if(flattened.constructor === Object && '@iri' in flattened)
+         if(flattened.constructor === Object && '@id' in flattened)
          {
             duplicate = (parent.filter(function(e)
             {
-               return (e.constructor === Object && '@iri' in e &&
-                  e['@iri'] === flattened['@iri']);
+               return (e.constructor === Object && '@id' in e &&
+                  e['@id'] === flattened['@id']);
             }).length > 0);
          }
          if(!duplicate)
@@ -1694,14 +1684,11 @@
    for(var i in bnodes)
    {
       var bnode = bnodes[i];
-      if(!('@subject' in bnode))
+      if(!('@id' in bnode))
       {
          // generate names until one is unique
-         while(ng.next() in subjects);
-         bnode['@subject'] =
-         {
-            '@iri': ng.current()
-         };
+         while(ng.next() in subjects){}
+         bnode['@id'] = ng.current();
          subjects[ng.current()] = bnode;
       }
    }
@@ -1716,10 +1703,10 @@
  */
 Processor.prototype.renameBlankNode = function(b, id)
 {
-   var old = b['@subject']['@iri'];
+   var old = b['@id'];
    
    // update bnode IRI
-   b['@subject']['@iri'] = id;
+   b['@id'] = id;
    
    // update subjects map
    var subjects = this.subjects;
@@ -1756,9 +1743,9 @@
             for(var n in tmp)
             {
                if(tmp[n].constructor === Object &&
-                  '@iri' in tmp[n] && tmp[n]['@iri'] === old)
+                  '@id' in tmp[n] && tmp[n]['@id'] === old)
                {
-                  tmp[n]['@iri'] = id;
+                  tmp[n]['@id'] = id;
                }
             }
          }
@@ -1803,7 +1790,7 @@
    var bnodes = [];
    for(var i in input)
    {
-      var iri = input[i]['@subject']['@iri'];
+      var iri = input[i]['@id'];
       subjects[iri] = input[i];
       edges.refs[iri] =
       {
@@ -1833,13 +1820,13 @@
    for(var i in bnodes)
    {
       var bnode = bnodes[i];
-      var iri = bnode['@subject']['@iri'];
+      var iri = bnode['@id'];
       if(c14n.inNamespace(iri))
       {
          // generate names until one is unique
-         while(ngTmp.next() in subjects);
+         while(ngTmp.next() in subjects){};
          this.renameBlankNode(bnode, ngTmp.current());
-         iri = bnode['@subject']['@iri'];
+         iri = bnode['@id'];
       }
       this.serializations[iri] =
       {
@@ -1864,7 +1851,7 @@
       
       // name all bnodes according to the first bnode's relation mappings
       var bnode = bnodes.shift();
-      var iri = bnode['@subject']['@iri'];
+      var iri = bnode['@id'];
       var dirs = ['props', 'refs'];
       for(var d in dirs)
       {
@@ -1906,7 +1893,7 @@
          for(var i in tmp)
          {
             var b = tmp[i];
-            var iriB = b['@subject']['@iri'];
+            var iriB = b['@id'];
             if(!c14n.inNamespace(iriB))
             {
                // mark serializations related to the named bnodes as dirty
@@ -2014,7 +2001,7 @@
    var first = true;
    for(var p in b)
    {
-      if(p !== '@subject')
+      if(p !== '@id')
       {
          if(first)
          {
@@ -2035,16 +2022,16 @@
             var o = objs[oi];
             if(o.constructor === Object)
             {
-               // iri
-               if('@iri' in o)
+               // ID (IRI)
+               if('@id' in o)
                {
-                  if(_isBlankNodeIri(o['@iri']))
+                  if(_isBlankNodeIri(o['@id']))
                   {
                      rval += '_:';
                   }
                   else
                   {
-                     rval += '<' + o['@iri'] + '>';
+                     rval += '<' + o['@id'] + '>';
                   }
                }
                // literal
@@ -2052,10 +2039,10 @@
                {
                   rval += '"' + o['@literal'] + '"';
                   
-                  // datatype literal
-                  if('@datatype' in o)
+                  // type literal
+                  if('@type' in o)
                   {
-                     rval += '^^<' + o['@datatype'] + '>';
+                     rval += '^^<' + o['@type'] + '>';
                   }
                   // language literal
                   else if('@language' in o)
@@ -2338,12 +2325,12 @@
             {
                if(!same)
                {
-                  mapped[mb.mapNode(prev['@subject'])] = prev['@subject'];
+                  mapped[mb.mapNode(prev['@id'])] = prev['@id'];
                   delete notMapped[i - 1];
                }
                if(i === notMapped.length - 1)
                {
-                  mapped[mb.mapNode(curr['@subject'])];
+                  mapped[mb.mapNode(curr['@id'])];
                   delete notMapped[i];
                }
                same = false;
@@ -2394,8 +2381,8 @@
    var rval = 0;
    
    // compare IRIs
-   var iriA = a['@subject']['@iri'];
-   var iriB = b['@subject']['@iri'];
+   var iriA = a['@id'];
+   var iriB = b['@id'];
    if(iriA === iriB)
    {
       rval = 0;
@@ -2494,8 +2481,8 @@
    // step #4
    if(rval === 0)
    {
-      var edgesA = this.edges.refs[a['@subject']['@iri']].all;
-      var edgesB = this.edges.refs[b['@subject']['@iri']].all;
+      var edgesA = this.edges.refs[a['@id']].all;
+      var edgesB = this.edges.refs[b['@id']].all;
       rval = _compare(edgesA.length, edgesB.length);
    }
    
@@ -2584,7 +2571,7 @@
       var subject = this.subjects[iri];
       for(var key in subject)
       {
-         if(key !== '@subject')
+         if(key !== '@id')
          {
             // normalize to array for single codepath
             var object = subject[key];
@@ -2592,10 +2579,10 @@
             for(var i in tmp)
             {
                var o = tmp[i];
-               if(o.constructor === Object && '@iri' in o &&
-                  o['@iri'] in this.subjects)
+               if(o.constructor === Object && '@id' in o &&
+                  o['@id'] in this.subjects)
                {
-                  var objIri = o['@iri'];
+                  var objIri = o['@id'];
                   
                   // map object to this subject
                   refs[objIri].all.push({ s: iri, p: key });
@@ -2614,7 +2601,7 @@
    {
       refs[iri].all.sort(function(a, b) { return self.compareEdges(a, b); });
       refs[iri].bnodes = refs[iri].all.filter(function(edge) {
-         return _isBlankNodeIri(edge.s)
+         return _isBlankNodeIri(edge.s);
       });
    }
    for(var iri in props)
@@ -2642,7 +2629,7 @@
    // check if type(s) are specified in frame and input
    var type = '@type';
    if('@type' in frame &&
-      input.constructor === Object && '@subject' in input && type in input)
+      input.constructor === Object && type in input)
    {
       var tmp = (input[type].constructor === Array) ?
          input[type] : [input[type]];
@@ -2650,10 +2637,10 @@
          frame[type] : [frame[type]];
       for(var t = 0; t < types.length && !rval; ++t)
       {
-         type = types[t]['@iri'];
+         type = types[t];
          for(var i in tmp)
          {
-            if(tmp[i]['@iri'] === type)
+            if(tmp[i] === type)
             {
                rval = true;
                break;
@@ -2693,7 +2680,7 @@
          rval = true;
       }
       // input must be a subject with all the given properties
-      else if(input.constructor === Object && '@subject' in input)
+      else if(input.constructor === Object && '@id' in input)
       {
          rval = true;
          for(var i in props)
@@ -2728,7 +2715,7 @@
    subjects, value, frame, embeds, autoembed, parent, parentKey, options)
 {
    // get existing embed entry
-   var iri = value['@subject']['@iri'];
+   var iri = value['@id'];
    var embed = (iri in embeds) ? embeds[iri] : null;
    
    // determine if value should be embedded or referenced,
@@ -2744,7 +2731,7 @@
    if(!embedOn)
    {
       // not embedding, so only use subject IRI as reference
-      value = value['@subject'];
+      value = {'@id': value['@id']};
    }
    else
    {
@@ -2763,17 +2750,17 @@
             var objs = embed.parent[embed.key];
             for(var i in objs)
             {
-               if(objs[i].constructor === Object && '@subject' in objs[i] &&
-                  objs[i]['@subject']['@iri'] === iri)
+               if(objs[i].constructor === Object && '@id' in objs[i] &&
+                  objs[i]['@id'] === iri)
                {
-                  objs[i] = value['@subject'];
+                  objs[i] = {'@id': value['@id']};
                   break;
                }
             }
          }
          else
          {
-            embed.parent[embed.key] = value['@subject'];
+            embed.parent[embed.key] = {'@id': value['@id']};
          }
          
          // recursively remove any dependent dangling embeds
@@ -2784,7 +2771,7 @@
             {
                i = iris[i];
                if(i in embeds && embeds[i].parent !== null &&
-                  embeds[i].parent['@subject']['@iri'] === iri)
+                  embeds[i].parent['@id'] === iri)
                {
                   delete embeds[i];
                   removeDependents(i);
@@ -2800,15 +2787,15 @@
       embed.key = parentKey;
       
       // check explicit flag
-      var explicitOn =
-         frame['@explicit'] === true || options.defaults.explicitOn;
+      var explicitOn = (
+         frame['@explicit'] === true || options.defaults.explicitOn);
       if(explicitOn)
       {
          // remove keys from the value that aren't in the frame
          for(key in value)
          {
-            // do not remove @subject or any frame key
-            if(key !== '@subject' && !(key in frame))
+            // do not remove @id or any frame key
+            if(key !== '@id' && !(key in frame))
             {
                delete value[key];
             }
@@ -2819,9 +2806,9 @@
       var keys = Object.keys(value);
       for(i in keys)
       {
-         // skip keywords and type
+         // skip keywords
          var key = keys[i];
-         if(key.indexOf('@') !== 0 && key !== '@type')
+         if(key.indexOf('@') !== 0)
          {
             // get the subframe if available
             if(key in frame)
@@ -2841,12 +2828,12 @@
             var input = (v.constructor === Array) ? v : [v];
             for(var n in input)
             {
-               // replace reference to subject w/subject
+               // replace reference to subject w/embedded subject
                if(input[n].constructor === Object &&
-                  '@iri' in input[n] &&
-                  input[n]['@iri'] in subjects)
+                  '@id' in input[n] &&
+                  input[n]['@id'] in subjects)
                {
-                  input[n] = subjects[input[n]['@iri']];
+                  input[n] = subjects[input[n]['@id']];
                }
             }
             value[key] = _frame(
@@ -2857,9 +2844,8 @@
       // iterate over frame keys to add any missing values
       for(key in frame)
       {
-         // skip keywords, type query, and non-null keys in value
-         if(key.indexOf('@') !== 0 && key !== '@type' &&
-            (!(key in value) || value[key] === null))
+         // skip keywords and non-null keys in value
+         if(key.indexOf('@') !== 0 && (!(key in value) || value[key] === null))
          {
             var f = frame[key];
             
@@ -2878,8 +2864,8 @@
                }
                
                // determine if omit default is on
-               var omitOn =
-                  f['@omitDefault'] === true || options.defaults.omitDefaultOn;
+               var omitOn = (
+                  f['@omitDefault'] === true || options.defaults.omitDefaultOn);
                if(!omitOn)
                {
                   if('@default' in f)
@@ -2899,7 +2885,7 @@
    }
    
    return value;
-}
+};
 
 /**
  * Recursively frames the given input according to the given frame.
@@ -2976,7 +2962,7 @@
          var value = values[i1][i2];
          
          // if value is a subject, do subframing
-         if(value.constructor === Object && '@subject' in value)
+         if(_isSubject(value))
          {
             value = _subframe(
                subjects, value, frame, embeds, autoembed,
@@ -2990,9 +2976,8 @@
          }
          else
          {
-            // determine if value is a reference
-            var isRef = (value !== null && value.constructor === Object &&
-               '@iri' in value && value['@iri'] in embeds);
+            // determine if value is a reference to an embed
+            var isRef = (_isReference(value) && value['@id'] in embeds);
             
             // push any value that isn't a parentless reference
             if(!(parent === null && isRef))
@@ -3064,7 +3049,7 @@
    var subjects = {};
    for(var i in input)
    {
-      subjects[input[i]['@subject']['@iri']] = input[i];
+      subjects[input[i]['@id']] = input[i];
    }
    
    // frame input
--- a/playground/lang-jsonld.js	Thu Dec 15 16:55:21 2011 -0500
+++ b/playground/lang-jsonld.js	Tue Dec 20 00:07:26 2011 -0500
@@ -55,7 +55,7 @@
     ],
     [
       // common "keywords"
-      ["lang-jsonld-keyword", /^"(@iri|@coerce|@context|@subject|@type|@vocab|@base|@literal|@language|@datatype)"\s*:/],
+      ["lang-jsonld-keyword", /^"(@id|@context|@type|@literal|@language)"\s*:/],
       // empty string
       //[PR.PR_LITERAL, /^""/],
       ["lang-jsonld-string", /^""/],
--- a/playground/playground-examples.js	Thu Dec 15 16:55:21 2011 -0500
+++ b/playground/playground-examples.js	Tue Dec 20 00:07:26 2011 -0500
@@ -13,61 +13,55 @@
    playground.frames = {};
 
    // add the example of a Person
-   playground.examples["Person"] =
-   {
-      "@context":
-      {
+   playground.examples["Person"] = {
+      "@context": {
          "name": "http://xmlns.com/foaf/0.1/name",
-         "homepage": "http://xmlns.com/foaf/0.1/homepage",
-         "xsd": "http://www.w3.org/2001/XMLSchema#",
-         "@coerce":
-         {
-            "homepage": "@iri"
-         }
+         "homepage": {
+            "@id": "http://xmlns.com/foaf/0.1/homepage",
+            "@type": "@id"
+         },
+         "xsd": "http://www.w3.org/2001/XMLSchema#"
       },
       "name": "Manu Sporny",
       "homepage": "http://manu.sporny.org/"
    };
 
    // add the example of a Place
-   playground.examples["Place"] =
-   {
-      "@context":
-      {
+   playground.examples["Place"] = {
+      "@context": {
          "name": "http://schema.org/name",
          "description": "http://schema.org/description",
-         "image": "http://schema.org/image",
+         "image": {
+            "@id": "http://schema.org/image",
+            "@type": "@id"
+         },
          "geo": "http://schema.org/geo",
-         "latitude": "http://schema.org/latitude",
-         "longitude": "http://schema.org/longitude",
-         "xsd": "http://www.w3.org/2001/XMLSchema#",
-         "@coerce":
-         {
-            "image": "@iri",
-            "latitude": "xsd:float",
-            "longitude": "xsd:float"
-         }
+         "latitude": {
+            "@id": "http://schema.org/latitude",
+            "@type": "xsd:float"
+         },
+         "longitude": {
+            "@id": "http://schema.org/longitude",
+            "@type": "xsd:float"
+         },
+         "xsd": "http://www.w3.org/2001/XMLSchema#"
       },
       "name": "The Empire State Building",
       "description": "The Empire State Building is a 102-story landmark in New York City.",
       "image": "http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg",
-      "geo":
-      {
+      "geo": {
          "latitude": "40.75",
          "longitude": "73.98"
       }
    };
 
    // add the example of a Event
-   playground.examples["Event"] =
-   {
-      "@context":
-      {
+   playground.examples["Event"] = {
+      "@context": {
          "ical": "http://www.w3.org/2002/12/cal/ical#",
          "xsd": "http://www.w3.org/2001/XMLSchema#",
-         "@coerce":
-         {
-            "ical:dtstart": "xsd:dateTime"
+         "ical:dtstart": {
+            "@type": "xsd:dateTime"
          }
       },
       "ical:summary": "Lady Gaga Concert",
@@ -76,35 +70,36 @@
    };
 
    // add the example of a Product
-   playground.examples["Product"] =
-   {
-      "@context":
-      {
+   playground.examples["Product"] = {
+      "@context": {
          "gr": "http://purl.org/goodrelations/v1#",
          "pto": "http://www.productontology.org/id/",
          "foaf": "http://xmlns.com/foaf/0.1/",
          "xsd": "http://www.w3.org/2001/XMLSchema#",
-         "@coerce":
-         {
-            "foaf:page": "@iri",
-            "gr:acceptedPaymentMethods": "@iri",
-            "gr:hasBusinessFunction": "@iri",
-            "gr:hasCurrencyValue": "xsd:float"
+         "foaf:page": {
+            "@type": "@id"
+         },
+         "gr:acceptedPaymentMethods": {
+            "@type": "@id"
+         },
+         "gr:hasBusinessFunction": {
+            "@type": "@id"
+         },
+         "gr:hasCurrencyValue": {
+            "@type": "xsd:float"
          }
       },
-      "@subject": "http://example.org/cars/for-sale#tesla",
+      "@id": "http://example.org/cars/for-sale#tesla",
       "@type": "gr:Offering",
       "gr:name": "Used Tesla Roadster",
       "gr:description": "Need to sell fast and furiously",
       "gr:hasBusinessFunction": "gr:Sell",
       "gr:acceptedPaymentMethods": "gr:Cash",
-      "gr:hasPriceSpecification":
-      {
+      "gr:hasPriceSpecification": {
          "gr:hasCurrencyValue": "85000",
          "gr:hasCurrency": "USD"
       },
-      "gr:includes":
-      {
+      "gr:includes": {
          "@type": ["gr:Individual", "pto:Vehicle"],
          "gr:name": "Tesla Roadster",
          "foaf:page": "http://www.teslamotors.com/roadster"
@@ -112,101 +107,80 @@
    };
 
    // add the example of a Recipe
-   playground.examples["Recipe"] =
-   {
-      "@context":
-      {
+   playground.examples["Recipe"] = {
+      "@context": {
          "name": "http://rdf.data-vocabulary.org/#name",
          "ingredient": "http://rdf.data-vocabulary.org/#ingredients",
          "yield": "http://rdf.data-vocabulary.org/#yield",
          "instructions": "http://rdf.data-vocabulary.org/#instructions",
-         "step": "http://rdf.data-vocabulary.org/#step",
+         "step": {
+            "@id": "http://rdf.data-vocabulary.org/#step",
+            "@type": "xsd:integer"
+         },
          "description": "http://rdf.data-vocabulary.org/#description",
-         "xsd": "http://www.w3.org/2001/XMLSchema#",
-         "@coerce":
-         {
-            "step": "xsd:integer"
-         }
+         "xsd": "http://www.w3.org/2001/XMLSchema#"
       },
       "name": "Mojito",
       "ingredient": ["12 fresh mint leaves", "1/2 lime, juiced with pulp",
          "1 tablespoons white sugar", "1 cup ice cubes",
          "2 fluid ounces white rum", "1/2 cup club soda"],
       "yield": "1 cocktail",
-      "instructions" :
-      [
-         {
-            "step": 1,
-            "description": "Crush lime juice, mint and sugar together in glass."
-         },
-         {
-            "step": 2,
-            "description": "Fill glass to top with ice cubes."
-         },
-         {
-            "step": 3,
-            "description": "Pour white rum over ice."
-         },
-         {
-            "step": 4,
-            "description": "Fill the rest of glass with club soda, stir."
-         },
-         {
-            "step": 5,
-            "description": "Garnish with a lime wedge."
-         }
-      ]
+      "instructions" : [{
+         "step": 1,
+         "description": "Crush lime juice, mint and sugar together in glass."
+      }, {
+         "step": 2,
+         "description": "Fill glass to top with ice cubes."
+      }, {
+         "step": 3,
+         "description": "Pour white rum over ice."
+      }, {
+         "step": 4,
+         "description": "Fill the rest of glass with club soda, stir."
+      }, {
+         "step": 5,
+         "description": "Garnish with a lime wedge."
+      }]
    };
 
    // add the example of a Library
-   playground.examples["Library"] =
-   {
-      "@context":
-      {
-         "@coerce":
-         {
-            "ex:contains": "@iri"
-         },
+   playground.examples["Library"] = {
+      "@context": {
          "dc": "http://purl.org/dc/elements/1.1/",
          "ex": "http://example.org/vocab#",
-         "xsd": "http://www.w3.org/2001/XMLSchema#"
+         "xsd": "http://www.w3.org/2001/XMLSchema#",
+         "ex:contains": {
+            "@type": "@id"
+         }
       },
-      "@subject": [
-         {
-            "@subject": "http://example.org/library",
-            "@type": "ex:Library",
-            "ex:contains": "http://example.org/library/the-republic"
-         },
-         {
-            "@subject": "http://example.org/library/the-republic",
-            "@type": "ex:Book",
-            "dc:creator": "Plato",
-            "dc:title": "The Republic",
-            "ex:contains": "http://example.org/library/the-republic#introduction"
-         },
-         {
-            "@subject": "http://example.org/library/the-republic#introduction",
-            "@type": "ex:Chapter",
-            "dc:description": "An introductory chapter on The Republic.",
-            "dc:title": "The Introduction"
-         }
-      ]
+      "@id": [{
+         "@id": "http://example.org/library",
+         "@type": "ex:Library",
+         "ex:contains": "http://example.org/library/the-republic"
+      }, {
+         "@id": "http://example.org/library/the-republic",
+         "@type": "ex:Book",
+         "dc:creator": "Plato",
+         "dc:title": "The Republic",
+         "ex:contains": "http://example.org/library/the-republic#introduction"
+      }, {
+         "@id": "http://example.org/library/the-republic#introduction",
+         "@type": "ex:Chapter",
+         "dc:description": "An introductory chapter on The Republic.",
+         "dc:title": "The Introduction"
+      }]
    };
 
    // add the frame example of a Library
-   playground.frames["Library"] =
-   {
-      "@context":
-      {
+   playground.frames["Library"] = {
+      "@context": {
          "dc": "http://purl.org/dc/elements/1.1/",
          "ex": "http://example.org/vocab#"
       },
       "@type": "ex:Library",
-      "ex:contains":
-      {
+      "ex:contains": {
          "@type": "ex:Book",
-         "ex:contains":
-         {
+         "ex:contains": {
             "@type": "ex:Chapter"
          }
       }