lipdjs
Version:
A JavaScript library for reading and writing LiPD (Linked Paleo Data) files
125 lines (109 loc) • 3.76 kB
text/typescript
/**
* The RDFToJSON class helps in converting an RDF Graph to Plain JSON
* It uses the SCHEMA dictionary (from globals/schema.ts) to do the conversion
*/
import { Store } from 'n3';
import { DataFactory, Quad, NamedNode, Literal } from 'n3';
import { Logger } from '../utils/logger';
const logger = Logger.getInstance();
const DF = DataFactory;
interface PropertyValue {
'@type': 'uri' | 'literal';
'@id'?: string;
'@value'?: string;
'@datatype'?: string;
}
interface Facts {
[key: string]: PropertyValue[];
}
export class RDFToJSON {
private store: Store;
private id: string;
private facts: { [key: string]: Facts } = {};
/**
* Constructor for RDFToJSON class
* @param id The ID of the root node to start conversion from
* @param store The RDF graph to convert
*/
constructor(id: string, store: Store) {
this.id = id;
this.store = store;
this._getIndexedFacts(id);
logger.debug('RDFToJSON instance created with id: %s', id);
}
/**
* Get property values from query results
* @param qres Query results from RDF graph
* @returns Object containing property values
*/
private _getPropValuesFromQueryResultPO(quads: Quad[]): Facts {
const facts: Facts = {};
for (const quad of quads) {
const pname = this._localName(quad.predicate as NamedNode);
if (!facts[pname]) {
facts[pname] = [];
}
const value: PropertyValue = {
'@type': quad.object instanceof NamedNode ? 'uri' : 'literal'
};
if (quad.object instanceof NamedNode) {
value['@id'] = quad.object.value;
} else if (quad.object instanceof Literal) {
value['@value'] = quad.object.value;
if (quad.object.datatype) {
value['@datatype'] = quad.object.datatype.value;
}
}
facts[pname].push(value);
}
return facts;
}
/**
* Get facts for a given ID from the RDF graph
* @param id The ID to get facts for
* @returns Object containing facts
*/
private _getFacts(id: string): Facts {
const subject = DF.namedNode(id);
const quads = this.store.getQuads(subject, null, null, null);
return this._getPropValuesFromQueryResultPO(quads);
}
/**
* Get the local name from a URI
* @param url The URI to get the local name from
* @returns The local name
*/
private _localName(url: NamedNode): string {
const parts = url.value.split('#');
return parts[parts.length - 1];
}
/**
* Get indexed facts for a given ID and its related objects
* Recursively retrieves facts for all related objects
* @param id The ID to get indexed facts for
*/
private _getIndexedFacts(id: string): void {
// Skip if we've already processed this ID
if (id in this.facts) {
return;
}
// Get facts for this ID
const facts = this._getFacts(id);
this.facts[id] = facts;
// Recursively process all URI objects except for type predicates
for (const [pname, pfacts] of Object.entries(facts)) {
for (const pfact of pfacts) {
if (pfact["@type"] === "uri" && pname !== "type") {
this._getIndexedFacts(pfact["@id"] as string);
}
}
}
}
/**
* Convert the RDF graph to JSON string
* @returns JSON string representation of the RDF graph
*/
public toJson(): string {
return JSON.stringify(this.facts, null, 3);
}
}