Skip to content

Commit

Permalink
Support compound-literal rdfDirection option.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlehn committed Sep 7, 2023
1 parent e297249 commit c5af420
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 24 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# jsonld ChangeLog

## 8.x.x - 2023-xx-xx

### Added
- Support `compound-literal` `rdfDirection` option.

## 8.3.0 - 2023-09-06

### Added
Expand Down
83 changes: 77 additions & 6 deletions lib/fromRdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const {

// constants
const {
// RDF,
RDF,
RDF_LIST,
RDF_FIRST,
RDF_REST,
Expand Down Expand Up @@ -61,19 +61,21 @@ api.fromRDF = async (
const defaultGraph = {};
const graphMap = {'@default': defaultGraph};
const referencedOnce = {};
let processCompoundLiterals = false;
if(rdfDirection) {
if(rdfDirection === 'compound-literal') {
throw new JsonLdError(
'Unsupported rdfDirection value.',
'jsonld.InvalidRdfDirection',
{value: rdfDirection});
processCompoundLiterals = true;
} else if(rdfDirection !== 'i18n-datatype') {
throw new JsonLdError(
'Unknown rdfDirection value.',
'jsonld.InvalidRdfDirection',
{value: rdfDirection});
}
}
let compoundLiteralSubjects;
if(processCompoundLiterals) {
compoundLiteralSubjects = {};
}

for(const quad of dataset) {
// TODO: change 'name' to 'graph'
Expand All @@ -82,11 +84,18 @@ api.fromRDF = async (
if(!(name in graphMap)) {
graphMap[name] = {};
}
if(processCompoundLiterals && !(name in compoundLiteralSubjects)) {
compoundLiteralSubjects[name] = {};
}
if(name !== '@default' && !(name in defaultGraph)) {
defaultGraph[name] = {'@id': name};
}

const nodeMap = graphMap[name];
let compoundMap;
if(processCompoundLiterals) {
compoundMap = compoundLiteralSubjects[name];
}

// get subject, predicate, object
const s = quad.subject.value;
Expand All @@ -97,6 +106,9 @@ api.fromRDF = async (
nodeMap[s] = {'@id': s};
}
const node = nodeMap[s];
if(processCompoundLiterals && p === RDF + 'direction') {
compoundMap[s] = true;
}

const objectIsNode = o.termType.endsWith('Node');
if(objectIsNode && !(o.value in nodeMap)) {
Expand Down Expand Up @@ -208,6 +220,64 @@ api.fromRDF = async (
for(const name in graphMap) {
const graphObject = graphMap[name];

if(processCompoundLiterals) {
if(name in compoundLiteralSubjects) {
const cls = compoundLiteralSubjects[name];
for(const cl of Object.keys(cls)) {
const clEntry = referencedOnce[cl];
if(!clEntry) {
continue;
}
const node = clEntry.node;
const property = clEntry.property;
//const value = clEntry.value;
const clNode = graphObject[cl];
if(!types.isObject(clNode)) {
continue;
}
delete graphObject[cl];
for(const clReference of node[property]) {
if(clReference['@id'] === cl) {
delete clReference['@id'];
}
const value = clNode[RDF + 'value'];
// FIXME: error on !== 1 value
clReference['@value'] = value[0]['@value'];
const language = clNode[RDF + 'language'];
if(language) {
// FIXME: error on !== 1 language value
const v = language[0]['@value'];
if(!v.match(REGEX_BCP47)) {
throw new JsonLdError(
'Invalid RDF syntax; rdf:language must be valid BCP47.',
'jsonld.SyntaxError',
{
code: 'invalid language-tagged string',
value: v
});
}
clReference['@language'] = v;
}
const direction = clNode[RDF + 'direction'];
if(direction) {
// FIXME: error on !== 1 direction value
const v = direction[0]['@value'];
if(!(v === 'ltr' || v === 'rtl')) {
throw new JsonLdError(
'Invalid RDF syntax; rdf:direction must be "ltr" or "rtl".',
'jsonld.SyntaxError',
{
code: 'invalid base direction',
value: v
});
}
clReference['@direction'] = v;
}
}
}
}
}

// no @lists to be converted, continue
if(!(RDF_NIL in graphObject)) {
continue;
Expand Down Expand Up @@ -296,7 +366,8 @@ api.fromRDF = async (
*
* @param o the RDF triple object to convert.
* @param useNativeTypes true to output native types, false not to.
* @param rdfDirection text direction mode [null, i18n-datatype]
* @param rdfDirection text direction mode [null, i18n-datatype,
* compound-literal]
* @param options top level API options
*
* @return the JSON-LD object.
Expand Down
9 changes: 6 additions & 3 deletions lib/jsonld.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,8 @@ jsonld.link = async function(input, ctx, options) {
* 'application/n-quads' for N-Quads.
* [documentLoader(url, options)] the document loader.
* [useNative] true to use a native canonize algorithm
* [rdfDirection] null or 'i18n-datatype' to support RDF
* [rdfDirection] Mode for RDF transformation of @direction. null,
* 'i18n-datatype', or 'compound-literal' (default: null).
* transformation of @direction (default: null).
* [safe] true to use safe mode. (default: true).
* [contextResolver] internal use only.
Expand Down Expand Up @@ -605,7 +606,8 @@ jsonld.normalize = jsonld.canonize = async function(input, options) {
* (default: false).
* [useNativeTypes] true to convert XSD types into native types
* (boolean, integer, double), false not to (default: false).
* [rdfDirection] null or 'i18n-datatype' to support RDF
* [rdfDirection] Mode for RDF transformation of @direction. null,
* 'i18n-datatype', or 'compound-literal' (default: null).
* transformation of @direction (default: null).
* [safe] true to use safe mode. (default: false)
*
Expand Down Expand Up @@ -659,7 +661,8 @@ jsonld.fromRDF = async function(dataset, options) {
* to produce only standard RDF (default: false).
* [documentLoader(url, options)] the document loader.
* [safe] true to use safe mode. (default: false)
* [rdfDirection] null or 'i18n-datatype' to support RDF
* [rdfDirection] Mode for RDF transformation of @direction. null,
* 'i18n-datatype', or 'compound-literal' (default: null).
* transformation of @direction (default: null).
* [contextResolver] internal use only.
*
Expand Down
74 changes: 69 additions & 5 deletions lib/toRdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const {
} = require('./events');

const {
// RDF,
RDF,
// RDF_LIST,
RDF_FIRST,
RDF_REST,
Expand Down Expand Up @@ -320,10 +320,74 @@ function _objectToRDF(
object.datatype.value = datatype;
object.value = value;
} else if('@direction' in item && rdfDirection === 'compound-literal') {
throw new JsonLdError(
'Unsupported rdfDirection value.',
'jsonld.InvalidRdfDirection',
{value: rdfDirection});
const language = (item['@language'] || '').toLowerCase();
const direction = item['@direction'];
// blank node
object.termType = 'BlankNode';
object.value = issuer.getId();
object.datatype = undefined;
// value
dataset.push({
subject: {
termType: object.termType,
value: object.value
},
predicate: {
termType: 'NamedNode',
value: RDF + 'value'
},
object: {
termType: 'Literal',
value,
datatype: {
termType: 'NamedNode',
value: XSD_STRING
}
},
graph: graphTerm
});
// language if preset
if(language !== '') {
dataset.push({
subject: {
termType: object.termType,
value: object.value
},
predicate: {
termType: 'NamedNode',
value: RDF + 'language'
},
object: {
termType: 'Literal',
value: language,
datatype: {
termType: 'NamedNode',
value: XSD_STRING
}
},
graph: graphTerm
});
}
// direction
dataset.push({
subject: {
termType: object.termType,
value: object.value
},
predicate: {
termType: 'NamedNode',
value: RDF + 'direction'
},
object: {
termType: 'Literal',
value: direction,
datatype: {
termType: 'NamedNode',
value: XSD_STRING
}
},
graph: graphTerm
});
} else if('@direction' in item && rdfDirection) {
throw new JsonLdError(
'Unknown rdfDirection value.',
Expand Down
Loading

0 comments on commit c5af420

Please sign in to comment.