lincd-jsonld
Version:
Utilities to parse and write JSON-LD for the LINCD library
340 lines • 15.3 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { NodeURIMappings } from 'lincd/collections/NodeURIMappings';
import { CoreMap } from 'lincd/collections/CoreMap';
import { Graph, Literal, NamedNode, Quad } from 'lincd/models';
import { NodeSet } from 'lincd/collections/NodeSet';
import { CoreSet } from 'lincd/collections/CoreSet';
import { JSONLD } from './JSONLD.js';
import { QuadSet } from 'lincd/collections/QuadSet';
import { ShapeSet } from 'lincd/collections/ShapeSet';
import { linkedUtil } from '../package.js';
import { getShapeClass } from 'lincd/utils/ShapeClass';
import { QuadArray } from 'lincd/collections/QuadArray';
import { LinkedStorage } from 'lincd/utils/LinkedStorage';
let shapeErrorMessage = 'The class that generates it is likely not loaded into local memory. Import the class. On the backend, check your dependencies in package.json';
let JSONParser = class JSONParser {
static parse(json, targetGraph, setQuadsLoaded = false, nodemap = new NodeURIMappings()) {
try {
var object = JSON.parse(json);
}
catch (error) {
console.warn('Error parsing json: ');
console.trace(error.message);
}
return this.parseObject(object, targetGraph, setQuadsLoaded, nodemap);
}
static parseObject(object, targetGraph, setQuadsLoaded = false, nodemap = new NodeURIMappings(), overwriteData = false) {
if (!nodemap) {
nodemap = new NodeURIMappings();
}
var dataPromise;
if ((object instanceof Object && object !== null) ||
object instanceof Array) {
//update URI's before anything else
if ('__map' in object) {
var mappings = JSON.parse(object['__map']);
this.applyURIMappings(mappings);
// delete object['__map'];
}
//process data, which are quads
if ('__data' in object) {
dataPromise = JSONLD.parse(object['__data'], true, nodemap, targetGraph, overwriteData);
// delete object['__data'];
}
}
//set the target graph whilst parsing
this.targetGraph = targetGraph;
var contentPromise = this.parseObjectInternal(object, nodemap);
//reset the target graph for next run
delete this.targetGraph;
//make sure both the actual content-object is done AND its data
return Promise.all([dataPromise, contentPromise]).then(([dataResult, contentParseResult]) => {
//and return both, so the user can easily access the intended content but also the new quads
//combine all quads into one collection
let allQuads, content;
if (contentParseResult === null || contentParseResult === void 0 ? void 0 : contentParseResult.quads) {
content = contentParseResult.content;
allQuads = dataResult
? contentParseResult.quads.addFrom(dataResult.quads)
: contentParseResult.quads;
}
else {
content = contentParseResult;
allQuads = dataResult === null || dataResult === void 0 ? void 0 : dataResult.quads;
}
//if indicated to do so, update the cache of which property paths are now loaded based on these new quads
if (setQuadsLoaded && allQuads) {
LinkedStorage.setQuadsLoaded(allQuads);
}
return { content, quads: allQuads };
});
}
static parseObjectInternal(object, nodemap) {
if (typeof object === 'string' ||
typeof object === 'number' ||
typeof object === 'boolean') {
return Promise.resolve(object);
}
else if ((object instanceof Object && object !== null) ||
object instanceof Array) {
if ('__type' in object) {
var type = object['__type'];
if (type == 'ns') {
return this.createNodeSet(object, nodemap);
}
if (type == 'ss') {
return this.createShapeSet(object, nodemap);
}
if (type == 'cs') {
return this.createCoreSet(object, nodemap);
}
if (type == 'cm') {
return this.createCoreMap(object, nodemap);
}
if (type == 'qd') {
return this.createQuadSetData(object, nodemap);
}
}
else if ('__t' in object) {
return Promise.resolve(this.createQuad(object, nodemap, this.targetGraph));
}
else if ('__s' in object) {
return Promise.resolve(this.createShape(object, nodemap));
}
else if ('__sc' in object) {
return Promise.resolve(this.createShapeClass(object, nodemap));
}
else if ('__b' in object) {
return Promise.resolve(this.createBlankNode(object, nodemap));
}
else if ('__u' in object) {
return Promise.resolve(this.createNamedNode(object, nodemap));
}
else if ('__l' in object) {
return Promise.resolve(this.createLiteral(object));
}
else if ('__g' in object) {
return this.createGraph(object, nodemap);
}
else if ('__qs' in object) {
return this.createQuadSet(object, nodemap);
}
else if ('__qa' in object) {
return this.createQuadArray(object, nodemap);
}
else if ('__n' in object) {
// return this.parseNativeObject(object['__n'],nodemap);
return this.parseObjectInternal(object['__n'], nodemap);
}
else {
return this.parseNativeObject(object, nodemap);
}
}
else if (!object) {
return Promise.resolve(null);
}
else {
throw Error('Unable to parse this object: ' + object.toString());
}
}
static parseNativeObject(object, nodemap) {
//this was a native javascript object whose values may still need to be converted
var valuePromises = [];
//we create a new object for the result and leave the initial one intact
let resultObject = Array.isArray(object) ? [] : {};
let quads = new QuadSet();
var setValue = (key, keyParseResult) => {
if (keyParseResult === null || keyParseResult === void 0 ? void 0 : keyParseResult.quads) {
resultObject[key] = keyParseResult.content;
quads.addFrom(keyParseResult.quads);
}
else {
resultObject[key] = keyParseResult;
}
};
for (let key in object) {
valuePromises.push(this.parseObjectInternal(object[key], nodemap).then(setValue.bind(null, key)));
}
return Promise.all(valuePromises)
.then(() => {
return quads.size > 0 ? { content: resultObject, quads } : resultObject;
})
.catch((e) => {
console.warn('Error in parsing object keys');
throw e;
});
}
static createQuad(object, nodemap, targetGraph) {
var [subjectUri, predicateUri, objectString, graphString] = object['__t'];
var subject = this.createNamedNodeFromString(subjectUri, nodemap);
var predicate = this.createNamedNodeFromString(predicateUri, nodemap);
var objectNode = this.createNodeFromString(objectString, nodemap);
let graphNode, graph;
if (graphString) {
graphNode = this.createNodeFromString(graphString, nodemap);
graph = Graph.getOrCreate(graphNode.value);
}
else {
graph = targetGraph;
}
let q = Quad.getOrCreate(subject, predicate, objectNode, graph);
return { content: q, quads: [q] };
}
static createNodeFromString(str, nodemap) {
if (str.substr(0, 1) == '"') {
return Literal.fromString(str);
}
return this.createNamedNodeFromString(str, nodemap);
}
static createNamedNodeFromString(str, nodemap) {
//TODO: move this check into general "nodemap" method
if (str.substr(0, 1) == '_') {
return nodemap.getOrCreateBlankNode(str);
}
return nodemap.getOrCreateNamedNode(str);
}
static createShape(object, nodemap) {
// throw new Error('Outdated. Support for SHAPES still needs to be added');
var node = this.createNamedNodeFromString(object['u'], nodemap);
var nodeShape = this.createNamedNodeFromString(object['__s'], nodemap);
var ShapeClass = getShapeClass(nodeShape);
if (!ShapeClass) {
this.throwShapeError(nodeShape);
}
return new ShapeClass(node);
}
static throwShapeError(nodeShape) {
//TODO add link to documentation
throw Error(`You need to import this shape: ${nodeShape.uri}.
Could not convert JSON back to this shape, probably because the shape is not loaded.
Make sure to IMPORT AND USE the shape in the code that receives this shape.`);
}
static createShapeClass(object, nodemap) {
var nodeShape = this.createNamedNodeFromString(object['__sc'], nodemap);
var ShapeClass = getShapeClass(nodeShape);
if (!ShapeClass) {
this.throwShapeError(nodeShape);
}
return ShapeClass;
}
static createNamedNode(object, nodemap) {
return nodemap.getOrCreateNamedNode(object['__u']);
}
static createBlankNode(object, nodemap) {
return nodemap.getOrCreateBlankNode(object['__b']);
}
static createLiteral(object) {
return Literal.fromString(object['__l']);
}
static createShapeSet(object, nodemap) {
// throw new Error('Converting ShapeSets to JSON is not yet supported');
return this.createCoreSet(object, nodemap, new ShapeSet());
}
static createNodeSet(object, nodemap) {
return this.createCoreSet(object, nodemap, new NodeSet());
}
static applyURIMappings(uriMappings) {
if (uriMappings && Array.isArray(uriMappings)) {
uriMappings.forEach(([oldUri, newUri]) => {
var resource = NamedNode.getNamedNode(oldUri);
//we check if a node with the old uri exists, because its uri may already have been updated through other channels (like sockets)
if (resource) {
throw Error('Updating uris through JSON needs to be reimplemented');
// NamedNode.updateUri(resource, newUri);
}
});
return true;
}
return false;
}
static createCoreSet(object, nodemap, resultSet = new CoreSet()) {
var entryPromises = [];
let quads = new QuadSet();
object.entries.forEach((entry) => {
entryPromises.push(this.parseObjectInternal(entry, nodemap).then((entryParseResult) => {
//if the entry was an internal object that also returned quads
if (entryParseResult && entryParseResult.quads) {
quads.addFrom(entryParseResult.quads);
resultSet.add(entryParseResult.content);
}
else {
resultSet.add(entryParseResult);
}
}));
});
return Promise.all(entryPromises).then(() => {
return quads.size > 0 ? { content: resultSet, quads } : resultSet;
});
}
static createCoreMap(object, nodemap) {
var res = new CoreMap();
let quads = new QuadSet();
var entryPromises = [];
object.entries.forEach((entry) => {
entryPromises.push(this.parseObjectInternal(entry, nodemap).then((entryParseResult) => {
//if the entry was an internal object that also returned quads
if (entryParseResult && entryParseResult.quads) {
//then we store those quads and return an object with content & quads at the end of this method
let [key, value] = entryParseResult.content;
quads.addFrom(entryParseResult.quads);
res.set(key, value);
return;
}
else {
let [key, value] = entryParseResult;
res.set(key, value);
}
}));
});
return Promise.all(entryPromises).then(() => quads.size > 0 ? { content: res, quads } : res);
}
static createGraph(object, nodemap) {
if (object.content) {
return JSONLD.parse(object.content, true, nodemap, this.targetGraph).then((parseResult) => {
var graph = Graph.getOrCreate(object.__g);
if (object.content) {
return JSONLD.parse(object.content).then((parseResult) => {
return { content: graph, quads: parseResult.quads };
});
}
//TODO: test this use case, do we ever send graphs over? If so, does it need its content directly inside that graph?
// or do we keep it in the (potentially temporary) given target graph, for memory management reasons? (this is what we do now)
// graph.setContents(parseResult.quads);
return graph;
});
}
else {
return Promise.resolve(Graph.getOrCreate(object.__g));
}
}
static createQuadSet(object, nodemap) {
return JSONLD.parse(object['__qs'], true, nodemap, this.targetGraph).then((parseResult) => {
return { content: parseResult.quads, quads: parseResult.quads };
});
}
static createQuadArray(object, nodemap) {
return JSONLD.parse(object['__qa'], true, nodemap, this.targetGraph).then((parseResult) => {
//@ts-ignore
let content = new QuadArray(...parseResult.quads);
return { content, quads: parseResult.quads };
});
}
static createQuadSetData(object, nodemap) {
return Promise.resolve(object.entries);
}
};
JSONParser = __decorate([
linkedUtil
], JSONParser);
export { JSONParser };
//# sourceMappingURL=JSONParser.js.map