Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for rr:subject #35

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/helper/vocabulary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function createNamespace(baseUri, localNames) {
const namespace = {};
for (const localName of localNames) {
namespace[localName] = `${baseUri}${localName}`;
}
return namespace;
}

const RR = createNamespace('http://www.w3.org/ns/r2rml#', [
'BlankNode',
'IRI',
'Literal',
]);

const RDF = createNamespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#', [
'type',
]);

module.exports.RR = RR;
module.exports.RDF = RDF;
10 changes: 7 additions & 3 deletions src/input-parser/JSONParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ class JsonParser {
getData(index, selector) {
const sel = selector.replace(/^PATH~/, '');
const splitter = sel.startsWith('[') ? '' : '.';
return JSONPath({
const arr = JSONPath({
path: `${this.paths[index]}${splitter}${sel}`,
json: this.json,
resultType: selector.startsWith('PATH~') ? 'pointer' : 'value',
})
.filter((e) => e !== null && e !== undefined) // null values are ignored (undefined shouldn't happens since input is json)
.map((e) => e.toString()); // return only strings
.filter((e) => e !== null && e !== undefined); // null values are ignored (undefined shouldn't happens since input is json)

if (arr.length === 1 && Array.isArray(arr[0])) {
return arr[0].map((e) => e.toString());
}
return arr.map((e) => e.toString());
}
}

Expand Down
86 changes: 36 additions & 50 deletions src/input-parser/parser.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const tags = require('language-tags');

const helper = require('./helper.js');
const { RR, RDF } = require('../helper/vocabulary');
const prefixhelper = require('../helper/prefixHelper.js');
const functionHelper = require('../function/function.js');
const XMLParser = require('./XMLParser.js');
Expand Down Expand Up @@ -92,30 +93,17 @@ const iterateFile = async (Parser, data, currObject, prefixes, options) => {
}
}
const functionClassMap = (subjectMap.class && Object.keys(subjectMap.class).length > 1) ? subjectMap.class : undefined;
let idTemplate;
if (subjectMap.template) {
idTemplate = subjectMap.template;
}
let reference;
if (subjectMap.reference) {
reference = subjectMap.reference;
}

let constant;
if (subjectMap.constant) {
constant = subjectMap.constant;
}

let result = [];
const iteratorNumber = Parser.getCount();
if (reference) {
if (subjectMap.reference) {
for (let i = 0; i < iteratorNumber; i++) {
if (functionClassMap) {
type = await helper.subjFunctionExecution(Parser, functionClassMap, prefixes, data, i, options);
}
let obj = {};
count++;
let nodes = getDataFromParser(Parser, i, reference, options);
let nodes = getDataFromParser(Parser, i, subjectMap.reference, options);
nodes = helper.addArray(nodes);
// eslint-disable-next-line no-loop-func
// needs to be done in sequence, since result.push() is done.
Expand All @@ -137,30 +125,30 @@ const iterateFile = async (Parser, data, currObject, prefixes, options) => {
}
}
}
} else if (idTemplate) {
} else if (subjectMap.template) {
count++;
for (let i = 0; i < iteratorNumber; i++) {
if (functionClassMap) {
type = await helper.subjFunctionExecution(Parser, functionClassMap, prefixes, data, i, options);
}
let obj = {};
const ids = calculateTemplate(Parser, i, idTemplate, prefixes, undefined, options);
const ids = calculateTemplate(Parser, i, subjectMap.template, prefixes, undefined, options);
for (let id of ids) {
if (subjectMap.termType) {
const template = prefixhelper.replacePrefixWithURL(subjectMap.termType['@id'], prefixes);
switch (template) {
case 'http://www.w3.org/ns/r2rml#BlankNode':
case RR.BlankNode:
id = `_:${id}`;
break;
case 'http://www.w3.org/ns/r2rml#IRI':
if ((!idTemplate && !reference) || (idTemplate && reference)) {
case RR.IRI:
if ((!subjectMap.template && !subjectMap.reference) || (subjectMap.template && subjectMap.reference)) {
throw ('Must use exactly one of - rr:template and rr:reference in SubjectMap!');
}
if (!helper.isURL(id)) {
id = helper.addBase(id, prefixes);
}
break;
case 'http://www.w3.org/ns/r2rml#Literal':
case RR.Literal:
break;
default:
throw (`Don't know: ${subjectMap.termType['@id']}`);
Expand Down Expand Up @@ -191,31 +179,29 @@ const iterateFile = async (Parser, data, currObject, prefixes, options) => {
writeParentPath(Parser, i, parents, obj, options);
result.push(obj);
}
} else if (subjectMap.termType) {
const termType = prefixhelper.replacePrefixWithURL(subjectMap.termType['@id'], prefixes);
if (termType === 'http://www.w3.org/ns/r2rml#BlankNode') {
// BlankNode with no template or id
for (let i = 0; i < iteratorNumber; i++) {
if (functionClassMap) {
type = await helper.subjFunctionExecution(Parser, functionClassMap, prefixes, data, i, options);
}
count++;
let obj = {};
if (constant) {
obj['@id'] = helper.getConstant(constant, prefixes);
}
if (type) {
obj['@type'] = type;
}
obj = await doObjectMappings(Parser, i, currObject, data, prefixes, obj, options);
if (!obj['@id']) {
obj['@id'] = `_:${encodeURIComponent(`${currObject['@id']}_${count}`)}`;
}
writeParentPath(Parser, i, parents, obj, options);
result.push(obj);
} else if (subjectMap.constant || (
subjectMap.termType
&& prefixhelper.replacePrefixWithURL(subjectMap.termType['@id'], prefixes) === RR.BlankNode
)) {
// BlankNode with no template or id
for (let i = 0; i < iteratorNumber; i++) {
if (functionClassMap) {
type = await helper.subjFunctionExecution(Parser, functionClassMap, prefixes, data, i, options);
}
} else {
throw new Error('????');
count++;
let obj = {};
if (subjectMap.constant) {
obj['@id'] = helper.getConstant(subjectMap.constant, prefixes);
}
if (type) {
obj['@type'] = type;
}
obj = await doObjectMappings(Parser, i, currObject, data, prefixes, obj, options);
if (!obj['@id']) {
obj['@id'] = `_:${encodeURIComponent(`${currObject['@id']}_${count}`)}`;
}
writeParentPath(Parser, i, parents, obj, options);
result.push(obj);
}
} else {
throw new Error('Unsupported subjectmap');
Expand Down Expand Up @@ -308,12 +294,12 @@ const handleSingleMapping = async (Parser, index, obj, mapping, predicate, prefi
if (termtype) {
termtype = prefixhelper.replacePrefixWithURL(termtype, prefixes);
switch (termtype) {
case 'http://www.w3.org/ns/r2rml#BlankNode':
case RR.BlankNode:
t = {
'@id': `_:${t}`,
};
break;
case 'http://www.w3.org/ns/r2rml#IRI':
case RR.IRI:
if (!helper.isURL(t)) {
t = {
'@id': helper.addBase(t, prefixes),
Expand All @@ -324,7 +310,7 @@ const handleSingleMapping = async (Parser, index, obj, mapping, predicate, prefi
};
}
break;
case 'http://www.w3.org/ns/r2rml#Literal':
case RR.Literal:
break;
default:
throw (`Don't know: ${termtype['@id']}`);
Expand Down Expand Up @@ -354,7 +340,7 @@ const handleSingleMapping = async (Parser, index, obj, mapping, predicate, prefi
constant = helper.cutArray(constant);
constant = helper.getConstant(constant, prefixes);

if (prefixhelper.replacePrefixWithURL(predicate, prefixes) !== 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' && termtype && prefixhelper.replacePrefixWithURL(termtype, prefixes) === 'http://www.w3.org/ns/r2rml#IRI') {
if (prefixhelper.replacePrefixWithURL(predicate, prefixes) !== RDF.type && termtype && prefixhelper.replacePrefixWithURL(termtype, prefixes) === RR.IRI) {
if (!helper.isURL(constant)) {
constant = {
'@id': helper.addBase(constant, prefixes),
Expand Down Expand Up @@ -431,7 +417,7 @@ const calculateTemplate = (Parser, index, template, prefixes, termType, options)
for (const combin in allComb) {
let finTemp = template;
for (const found in allComb[combin]) {
if (!termType || termType !== 'http://www.w3.org/ns/r2rml#Literal') {
if (!termType || termType !== RR.Literal) {
allComb[combin][found] = helper.toURIComponent(allComb[combin][found]);
}
finTemp = finTemp.replace(`{${words[found]}}`, allComb[combin][found]);
Expand Down
4 changes: 4 additions & 0 deletions tests/arrayValueMapping/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name":"Tom A.",
"favorite-numbers": [ 3, 33, 13 ]
}
33 changes: 33 additions & 0 deletions tests/arrayValueMapping/mapping.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@prefix rr: <http://www.w3.org/ns/r2rml#> .
@prefix rml: <http://semweb.mmlab.be/ns/rml#> .
@prefix schema: <http://schema.org/> .
@prefix ql: <http://semweb.mmlab.be/ns/ql#> .
@base <http://sti2.at/> . #the base for the classes

<#LOGICALSOURCE>
rml:source "./tests/arrayValueMapping/input.json";
rml:referenceFormulation ql:JSONPath;
rml:iterator "$".


<#Mapping>
rml:logicalSource <#LOGICALSOURCE>;

rr:subjectMap [
rr:termType rr:BlankNode;
rr:class schema:Person;
];


rr:predicateObjectMap [
rr:predicateMap [ rr:constant "http://schema.org/name" ];
rr:objectMap [ rml:reference "name" ];
];

rr:predicateObjectMap [
rr:predicateMap [ rr:constant "http://example.com/favorite-numbers" ];
rr:objectMap [
rml:reference "favorite-numbers" ;
rr:datatype "http://www.w3.org/2001/XMLSchema#integer" ;
];
] .
21 changes: 21 additions & 0 deletions tests/arrayValueMapping/out.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"@type": "http://schema.org/Person",
"http://schema.org/name": "Tom A.",
"http://example.com/favorite-numbers": [
{
"@value": "3",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
},
{
"@type": "http://www.w3.org/2001/XMLSchema#integer",
"@value": "33"
},
{
"@type": "http://www.w3.org/2001/XMLSchema#integer",
"@value": "13"
}
],
"@id": "_:http%3A%2F%2Fsti2.at%2F%23Mapping_1"
}
]
4 changes: 4 additions & 0 deletions tests/constantSubjectMapping/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name":"Tom A.",
"age":15
}
29 changes: 29 additions & 0 deletions tests/constantSubjectMapping/mapping.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@prefix rr: <http://www.w3.org/ns/r2rml#> .
@prefix rml: <http://semweb.mmlab.be/ns/rml#> .
@prefix schema: <http://schema.org/> .
@prefix ql: <http://semweb.mmlab.be/ns/ql#> .
@base <http://sti2.at/> . #the base for the classes


<#LOGICALSOURCE>
rml:source "./tests/constantSubjectMapping/input.json";
rml:referenceFormulation ql:JSONPath;
rml:iterator "$".

<#Mapping>
rml:logicalSource <#LOGICALSOURCE>;

rr:subjectMap [
rr:constant "http://example.com/data/1234";
rr:class schema:Person;
];

rr:predicateObjectMap [
rr:predicateMap [rr:constant "http://schema.org/name"];
rr:objectMap [ rml:reference "name" ];
];

rr:predicateObjectMap [
rr:predicate schema:age;
rr:objectMap [ rml:reference "age" ];
].
8 changes: 8 additions & 0 deletions tests/constantSubjectMapping/out.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"@id": "http://example.com/data/1234",
"@type": "http://schema.org/Person",
"http://schema.org/name": "Tom A.",
"http://schema.org/age": "15"
}
]
26 changes: 26 additions & 0 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,32 @@ it('Basic straight double mapping', async () => {
assert.equal(result.length, 2);
});

it('Array Value mapping', async () => {
const options = {};
let result = await parser.parseFile('./tests/arrayValueMapping/mapping.ttl', './tests/arrayValueMapping/out.json', options).catch((err) => { console.log(err); });
result = helper.cutArray(result);
assert.equal(result['http://schema.org/name'], 'Tom A.');
assert.equal(result['http://example.com/favorite-numbers'].length, 3);
assert.equal(result['http://example.com/favorite-numbers'][0]['@value'], '3');
assert.equal(result['http://example.com/favorite-numbers'][0]['@type'], 'http://www.w3.org/2001/XMLSchema#integer');
assert.equal(result['http://example.com/favorite-numbers'][1]['@value'], '33');
assert.equal(result['http://example.com/favorite-numbers'][1]['@type'], 'http://www.w3.org/2001/XMLSchema#integer');
assert.equal(result['http://example.com/favorite-numbers'][2]['@value'], '13');
assert.equal(result['http://example.com/favorite-numbers'][2]['@type'], 'http://www.w3.org/2001/XMLSchema#integer');
assert.equal(result['@type'], 'http://schema.org/Person');
assert.equal(Object.keys(result).length, 4);
});

it('Constant subject mapping', async () => {
const options = {};
let result = await parser.parseFile('./tests/constantSubjectMapping/mapping.ttl', './tests/constantSubjectMapping/out.json', options).catch((err) => { console.log(err); });
result = helper.cutArray(result);
assert.equal(result['http://schema.org/name'], 'Tom A.');
assert.equal(result['@type'], 'http://schema.org/Person');
assert.equal(result['@id'], 'http://example.com/data/1234');
assert.equal(Object.keys(result).length, 4);
});

it('Live mapping', async () => {
const options = {
// baseMapping:["http://sti2.at/#SPORTSmapping"],
Expand Down