Skip to content

Commit c5af420

Browse files
committed
Support compound-literal rdfDirection option.
1 parent e297249 commit c5af420

File tree

6 files changed

+305
-24
lines changed

6 files changed

+305
-24
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# jsonld ChangeLog
22

3+
## 8.x.x - 2023-xx-xx
4+
5+
### Added
6+
- Support `compound-literal` `rdfDirection` option.
7+
38
## 8.3.0 - 2023-09-06
49

510
### Added

lib/fromRdf.js

+77-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const {
1818

1919
// constants
2020
const {
21-
// RDF,
21+
RDF,
2222
RDF_LIST,
2323
RDF_FIRST,
2424
RDF_REST,
@@ -61,19 +61,21 @@ api.fromRDF = async (
6161
const defaultGraph = {};
6262
const graphMap = {'@default': defaultGraph};
6363
const referencedOnce = {};
64+
let processCompoundLiterals = false;
6465
if(rdfDirection) {
6566
if(rdfDirection === 'compound-literal') {
66-
throw new JsonLdError(
67-
'Unsupported rdfDirection value.',
68-
'jsonld.InvalidRdfDirection',
69-
{value: rdfDirection});
67+
processCompoundLiterals = true;
7068
} else if(rdfDirection !== 'i18n-datatype') {
7169
throw new JsonLdError(
7270
'Unknown rdfDirection value.',
7371
'jsonld.InvalidRdfDirection',
7472
{value: rdfDirection});
7573
}
7674
}
75+
let compoundLiteralSubjects;
76+
if(processCompoundLiterals) {
77+
compoundLiteralSubjects = {};
78+
}
7779

7880
for(const quad of dataset) {
7981
// TODO: change 'name' to 'graph'
@@ -82,11 +84,18 @@ api.fromRDF = async (
8284
if(!(name in graphMap)) {
8385
graphMap[name] = {};
8486
}
87+
if(processCompoundLiterals && !(name in compoundLiteralSubjects)) {
88+
compoundLiteralSubjects[name] = {};
89+
}
8590
if(name !== '@default' && !(name in defaultGraph)) {
8691
defaultGraph[name] = {'@id': name};
8792
}
8893

8994
const nodeMap = graphMap[name];
95+
let compoundMap;
96+
if(processCompoundLiterals) {
97+
compoundMap = compoundLiteralSubjects[name];
98+
}
9099

91100
// get subject, predicate, object
92101
const s = quad.subject.value;
@@ -97,6 +106,9 @@ api.fromRDF = async (
97106
nodeMap[s] = {'@id': s};
98107
}
99108
const node = nodeMap[s];
109+
if(processCompoundLiterals && p === RDF + 'direction') {
110+
compoundMap[s] = true;
111+
}
100112

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

223+
if(processCompoundLiterals) {
224+
if(name in compoundLiteralSubjects) {
225+
const cls = compoundLiteralSubjects[name];
226+
for(const cl of Object.keys(cls)) {
227+
const clEntry = referencedOnce[cl];
228+
if(!clEntry) {
229+
continue;
230+
}
231+
const node = clEntry.node;
232+
const property = clEntry.property;
233+
//const value = clEntry.value;
234+
const clNode = graphObject[cl];
235+
if(!types.isObject(clNode)) {
236+
continue;
237+
}
238+
delete graphObject[cl];
239+
for(const clReference of node[property]) {
240+
if(clReference['@id'] === cl) {
241+
delete clReference['@id'];
242+
}
243+
const value = clNode[RDF + 'value'];
244+
// FIXME: error on !== 1 value
245+
clReference['@value'] = value[0]['@value'];
246+
const language = clNode[RDF + 'language'];
247+
if(language) {
248+
// FIXME: error on !== 1 language value
249+
const v = language[0]['@value'];
250+
if(!v.match(REGEX_BCP47)) {
251+
throw new JsonLdError(
252+
'Invalid RDF syntax; rdf:language must be valid BCP47.',
253+
'jsonld.SyntaxError',
254+
{
255+
code: 'invalid language-tagged string',
256+
value: v
257+
});
258+
}
259+
clReference['@language'] = v;
260+
}
261+
const direction = clNode[RDF + 'direction'];
262+
if(direction) {
263+
// FIXME: error on !== 1 direction value
264+
const v = direction[0]['@value'];
265+
if(!(v === 'ltr' || v === 'rtl')) {
266+
throw new JsonLdError(
267+
'Invalid RDF syntax; rdf:direction must be "ltr" or "rtl".',
268+
'jsonld.SyntaxError',
269+
{
270+
code: 'invalid base direction',
271+
value: v
272+
});
273+
}
274+
clReference['@direction'] = v;
275+
}
276+
}
277+
}
278+
}
279+
}
280+
211281
// no @lists to be converted, continue
212282
if(!(RDF_NIL in graphObject)) {
213283
continue;
@@ -296,7 +366,8 @@ api.fromRDF = async (
296366
*
297367
* @param o the RDF triple object to convert.
298368
* @param useNativeTypes true to output native types, false not to.
299-
* @param rdfDirection text direction mode [null, i18n-datatype]
369+
* @param rdfDirection text direction mode [null, i18n-datatype,
370+
* compound-literal]
300371
* @param options top level API options
301372
*
302373
* @return the JSON-LD object.

lib/jsonld.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,8 @@ jsonld.link = async function(input, ctx, options) {
547547
* 'application/n-quads' for N-Quads.
548548
* [documentLoader(url, options)] the document loader.
549549
* [useNative] true to use a native canonize algorithm
550-
* [rdfDirection] null or 'i18n-datatype' to support RDF
550+
* [rdfDirection] Mode for RDF transformation of @direction. null,
551+
* 'i18n-datatype', or 'compound-literal' (default: null).
551552
* transformation of @direction (default: null).
552553
* [safe] true to use safe mode. (default: true).
553554
* [contextResolver] internal use only.
@@ -605,7 +606,8 @@ jsonld.normalize = jsonld.canonize = async function(input, options) {
605606
* (default: false).
606607
* [useNativeTypes] true to convert XSD types into native types
607608
* (boolean, integer, double), false not to (default: false).
608-
* [rdfDirection] null or 'i18n-datatype' to support RDF
609+
* [rdfDirection] Mode for RDF transformation of @direction. null,
610+
* 'i18n-datatype', or 'compound-literal' (default: null).
609611
* transformation of @direction (default: null).
610612
* [safe] true to use safe mode. (default: false)
611613
*
@@ -659,7 +661,8 @@ jsonld.fromRDF = async function(dataset, options) {
659661
* to produce only standard RDF (default: false).
660662
* [documentLoader(url, options)] the document loader.
661663
* [safe] true to use safe mode. (default: false)
662-
* [rdfDirection] null or 'i18n-datatype' to support RDF
664+
* [rdfDirection] Mode for RDF transformation of @direction. null,
665+
* 'i18n-datatype', or 'compound-literal' (default: null).
663666
* transformation of @direction (default: null).
664667
* [contextResolver] internal use only.
665668
*

lib/toRdf.js

+69-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const {
1616
} = require('./events');
1717

1818
const {
19-
// RDF,
19+
RDF,
2020
// RDF_LIST,
2121
RDF_FIRST,
2222
RDF_REST,
@@ -320,10 +320,74 @@ function _objectToRDF(
320320
object.datatype.value = datatype;
321321
object.value = value;
322322
} else if('@direction' in item && rdfDirection === 'compound-literal') {
323-
throw new JsonLdError(
324-
'Unsupported rdfDirection value.',
325-
'jsonld.InvalidRdfDirection',
326-
{value: rdfDirection});
323+
const language = (item['@language'] || '').toLowerCase();
324+
const direction = item['@direction'];
325+
// blank node
326+
object.termType = 'BlankNode';
327+
object.value = issuer.getId();
328+
object.datatype = undefined;
329+
// value
330+
dataset.push({
331+
subject: {
332+
termType: object.termType,
333+
value: object.value
334+
},
335+
predicate: {
336+
termType: 'NamedNode',
337+
value: RDF + 'value'
338+
},
339+
object: {
340+
termType: 'Literal',
341+
value,
342+
datatype: {
343+
termType: 'NamedNode',
344+
value: XSD_STRING
345+
}
346+
},
347+
graph: graphTerm
348+
});
349+
// language if preset
350+
if(language !== '') {
351+
dataset.push({
352+
subject: {
353+
termType: object.termType,
354+
value: object.value
355+
},
356+
predicate: {
357+
termType: 'NamedNode',
358+
value: RDF + 'language'
359+
},
360+
object: {
361+
termType: 'Literal',
362+
value: language,
363+
datatype: {
364+
termType: 'NamedNode',
365+
value: XSD_STRING
366+
}
367+
},
368+
graph: graphTerm
369+
});
370+
}
371+
// direction
372+
dataset.push({
373+
subject: {
374+
termType: object.termType,
375+
value: object.value
376+
},
377+
predicate: {
378+
termType: 'NamedNode',
379+
value: RDF + 'direction'
380+
},
381+
object: {
382+
termType: 'Literal',
383+
value: direction,
384+
datatype: {
385+
termType: 'NamedNode',
386+
value: XSD_STRING
387+
}
388+
},
389+
graph: graphTerm
390+
});
327391
} else if('@direction' in item && rdfDirection) {
328392
throw new JsonLdError(
329393
'Unknown rdfDirection value.',

0 commit comments

Comments
 (0)