--- a/playground/jsonld.js Mon Apr 30 14:09:12 2012 -0400
+++ b/playground/jsonld.js Mon Apr 30 14:10:38 2012 -0400
@@ -423,6 +423,8 @@
*
* @param input the JSON-LD input.
* @param [options] the options to use:
+ * [format] the format to use to output a string:
+ * 'text/x-nquads' for N-Quads (default).
* [resolver(url, callback(err, jsonCtx))] the URL resolver to use.
* @param callback(err, statement) called when a statement is output, with the
* last statement as null.
@@ -436,7 +438,7 @@
options = arguments[1] || {};
callbackArg += 1;
}
- callback = arguments[callbackArg];
+ var cb = callback = arguments[callbackArg];
// set default options
if(!('base' in options)) {
@@ -446,6 +448,26 @@
options.resolver = jsonld.urlResolver;
}
+ if('format' in options) {
+ // supported formats
+ if(options.format === 'text/x-nquads') {
+ cb = function(err, statement) {
+ if(err) {
+ return callback(err);
+ }
+ if(statement !== null) {
+ statement = _toNQuad(statement);
+ }
+ callback(null, statement);
+ };
+ }
+ else {
+ throw new JsonLdError(
+ 'Unknown output format.',
+ 'jsonld.UnknownFormat', {format: options.format});
+ }
+ }
+
// expand input
jsonld.expand(input, options, function(err, expanded) {
if(err) {
@@ -454,9 +476,15 @@
'jsonld.RdfError', {cause: err}));
}
- // output RDF statements
- var namer = new UniqueNamer('_:t');
- new Processor().toRDF(expanded, namer, null, null, null, callback);
+ try {
+ // output RDF statements
+ var namer = new UniqueNamer('_:t');
+ new Processor().toRDF(expanded, namer, null, null, null, cb);
+ cb(null, null);
+ }
+ catch(ex) {
+ cb(ex);
+ }
});
};
@@ -1730,7 +1758,7 @@
// get subject @id (generate one if it is a bnode)
var isBnode = _isBlankNode(element);
- var id = isBnode ? namer.getName(input['@id']) : input['@id'];
+ var id = isBnode ? namer.getName(element['@id']) : element['@id'];
// create object
var object = {
@@ -1757,11 +1785,11 @@
// recurse over subject properties in order
var props = Object.keys(element).sort();
for(var pi in props) {
- var prop = props[pi];
+ var prop = p = props[pi];
// convert @type to rdf:type
if(prop === '@type') {
- prop = RDF_TYPE;
+ p = RDF_TYPE;
}
// recurse into @graph
@@ -1771,13 +1799,13 @@
}
// skip keywords
- if(_isKeyword(prop)) {
+ if(_isKeyword(p)) {
continue;
}
// create new active property
property = {
- nominalValue: id,
+ nominalValue: p,
interfaceName: 'IRI'
};
@@ -1789,13 +1817,14 @@
}
if(_isString(element)) {
- // emit plain literal
+ // emit IRI for rdf:type, else plain literal
var statement = {
subject: _clone(subject),
property: _clone(property),
object: {
nominalValue: element,
- interfaceName: 'LiteralNode'
+ interfaceName: ((property.nominalValue === RDF_TYPE) ?
+ 'IRI': 'LiteralNode')
}
};
if(graph !== null) {
@@ -1810,16 +1839,16 @@
var datatype = XSD_BOOLEAN;
var value = String(element);
}
- else if(_isInteger(element)) {
+ else if(_isDouble(element)) {
+ var datatype = XSD_DOUBLE;
+ // printf('%1.15e') equivalent
+ var value = element.toExponential(15).replace(
+ /(e(?:\+|-))([0-9])$/, '$10$2');
+ }
+ else {
var datatype = XSD_INTEGER;
var value = String(element);
}
- else {
- var datatype = XSD_DOUBLE;
- // printf('%1.16e') equivalent
- var value = element.toExponential(16).replace(
- /(e(?:\+|-))([0-9])$/, '$10$2');
- }
// emit typed literal
var statement = {
@@ -2034,8 +2063,8 @@
}
// convert double to @value
else if(_isDouble(o)) {
- // do special JSON-LD double format, printf('%1.16e') JS equivalent
- o = o.toExponential(16).replace(/(e(?:\+|-))([0-9])$/, '$10$2');
+ // do special JSON-LD double format, printf('%1.15e') JS equivalent
+ o = o.toExponential(15).replace(/(e(?:\+|-))([0-9])$/, '$10$2');
o = {'@value': o, '@type': XSD_DOUBLE};
}
// convert integer to @value
@@ -3394,7 +3423,7 @@
}
// prepend base
- value = base + value;
+ value = _prependBase(base, value);
// value must now be an absolute IRI
if(!_isAbsoluteIri(value)) {
@@ -3461,13 +3490,31 @@
// prepend base to term
if(!_isUndefined(base)) {
- term = base + term;
+ term = _prependBase(base, term);
}
return term;
}
/**
+ * Prepends a base IRI to the given relative IRI.
+ *
+ * @param base the base IRI.
+ * @param iri the relative IRI.
+ *
+ * @return the absolute IRI.
+ */
+function _prependBase(base, iri) {
+ if(iri === '' || iri.indexOf('#') === 0) {
+ return base + iri;
+ }
+ else {
+ // prepend last directory for base
+ return base.substr(0, base.lastIndexOf('/') + 1) + iri;
+ }
+}
+
+/**
* Gets the initial context.
*
* @return the initial context.
@@ -4043,6 +4090,58 @@
}
/**
+ * Converts an RDF statement to an N-Quad string (a single quad).
+ *
+ * @param statement the RDF statement to convert.
+ *
+ * @return the N-Quad string.
+ */
+function _toNQuad(statement) {
+ var s = statement.subject;
+ var p = statement.property;
+ var o = statement.object;
+ var g = statement.name || null;
+
+ var quad = '';
+
+ // subject is an IRI or bnode
+ if(s.interfaceName === 'IRI') {
+ quad += '<' + s.nominalValue + '>';
+ }
+ else {
+ quad += s.nominalValue;
+ }
+
+ // property is always an IRI
+ quad += ' <' + p.nominalValue + '> ';
+
+ // object is IRI, bnode, or literal
+ if(o.interfaceName === 'IRI') {
+ quad += '<' + o.nominalValue + '>';
+ }
+ else if(o.interfaceName === 'BlankNode') {
+ quad += o.nominalValue;
+ }
+ else {
+ quad += '"' + o.nominalValue + '"';
+ if('datatype' in o) {
+ quad += '^^<' + o.datatype.nominalValue + '>';
+ }
+ else if('language' in o) {
+ quad += '@' + o.language;
+ }
+ }
+
+ // graph
+ if(g !== null) {
+ quad += ' <' + g.nominalValue + '>';
+ }
+
+ quad += ' .\n';
+ return quad;
+}
+
+/**
* Creates a new UniqueNamer. A UniqueNamer issues unique names, keeping
* track of any previously issued names.
*