UNPKG

sparqlxml-parse

Version:
229 lines 10.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SparqlXmlParser = void 0; const rdf_data_factory_1 = require("rdf-data-factory"); const saxes_1 = require("@rubensworks/saxes"); const readable_stream_1 = require("readable-stream"); /** * Parser for the SPARQL Query Results XML format. * @see https://www.w3.org/TR/rdf-sparql-XMLres/ */ class SparqlXmlParser { constructor(settings) { settings = settings || {}; this.dataFactory = settings.dataFactory || new rdf_data_factory_1.DataFactory(); this.prefixVariableQuestionMark = !!settings.prefixVariableQuestionMark; this.parseUnsupportedVersions = !!settings.parseUnsupportedVersions; } /** * If the given version is valid for this parser to handle. * @param version A version string. */ isValidVersion(version) { return this.parseUnsupportedVersions || SparqlXmlParser.SUPPORTED_VERSIONS.includes(version); } /** * Convert a SPARQL XML bindings response stream to a stream of bindings objects. * * The bindings stream will emit a 'variables' event that will contain * the array of variables (as RDF.Variable[]), as defined in the response head. * * @param {NodeJS.ReadableStream} sparqlResponseStream A SPARQL XML response stream. * @param version The version that was supplied as a media type parameter * @return {NodeJS.ReadableStream} A stream of bindings. */ parseXmlResultsStream(sparqlResponseStream, version) { const errorListener = (error) => resultStream.emit('error', error); sparqlResponseStream.on('error', errorListener); const parser = new saxes_1.SaxesParser(); const stack = []; let variablesFound = false; let resultsFound = false; const variables = []; let currentBindings = {}; let currentBindingName = ''; let currentBindingType = ''; let currentBindingAnnotation; let currentText = ''; let currentQuotedTriples = []; parser.on("error", errorListener); parser.on("opentag", tag => { if (tag.name === "variable" && this.stackEquals(stack, ['sparql', 'head'])) { variables.push(this.dataFactory.variable(tag.attributes.name)); } else if (tag.name === "results" && this.stackEquals(stack, ['sparql'])) { resultsFound = true; } else if (tag.name === 'result' && this.stackEquals(stack, ['sparql', 'results'])) { currentBindings = {}; } else if (tag.name === 'binding' && this.stackEquals(stack, ['sparql', 'results', 'result'])) { currentBindingName = tag.attributes.name || ''; currentBindingType = ''; currentBindingAnnotation = undefined; currentText = ''; currentQuotedTriples = []; } else if (tag.name === 'triple' && this.stackBeginsWith(stack, ['sparql', 'results', 'result'])) { currentQuotedTriples.push({ components: {} }); } else if (stack[stack.length - 1] === 'triple' && this.stackBeginsWith(stack, ['sparql', 'results', 'result', 'binding'])) { currentBindingType = ''; currentBindingAnnotation = undefined; currentText = ''; if (!['subject', 'predicate', 'object'].includes(tag.name)) { errorListener(new Error(`Illegal quoted triple component '${tag.name}' found on line ${parser.line + 1}`)); } else { currentQuotedTriples[currentQuotedTriples.length - 1].currentComponent = tag.name; } } else if (this.stackBeginsWith(stack, ['sparql', 'results', 'result', 'binding'])) { currentBindingType = tag.name; if ('xml:lang' in tag.attributes) { currentBindingAnnotation = { language: tag.attributes['xml:lang'], direction: tag.attributes['its:dir'], }; } else if ('datatype' in tag.attributes) { currentBindingAnnotation = this.dataFactory.namedNode(tag.attributes.datatype); } else { currentBindingAnnotation = undefined; } } else if (tag.name === 'sparql' && tag.attributes.version) { if (!this.isValidVersion(tag.attributes.version)) { resultStream.emit("error", new Error(`Detected unsupported version: ${tag.attributes.version}`)); } resultStream.emit('version', tag.attributes.version); } stack.push(tag.name); }); parser.on("closetag", tag => { if (this.stackEquals(stack, ['sparql', 'head'])) { resultStream.emit("variables", variables); variablesFound = true; } if (this.stackEquals(stack, ['sparql', 'results', 'result'])) { resultStream.push(currentBindings); } if (this.stackBeginsWith(stack, ['sparql', 'results', 'result', 'binding'])) { // Determine current RDF term value let term; if (!currentBindingName && currentBindingType) { errorListener(new Error(`Terms should have a name on line ${parser.line + 1}`)); } else if (currentBindingType === 'uri') { term = this.dataFactory.namedNode(currentText); } else if (currentBindingType === 'bnode') { term = this.dataFactory.blankNode(currentText); } else if (currentBindingType === 'literal') { term = this.dataFactory.literal(currentText, currentBindingAnnotation); } else if (stack[stack.length - 1] === 'triple') { const currentQuotedTriple = currentQuotedTriples.pop(); if (currentQuotedTriple && currentQuotedTriple.components.subject && currentQuotedTriple.components.predicate && currentQuotedTriple.components.object) { term = this.dataFactory.quad(currentQuotedTriple.components.subject, currentQuotedTriple.components.predicate, currentQuotedTriple.components.object); } else { errorListener(new Error(`Incomplete quoted triple on line ${parser.line + 1}`)); } } else if (currentBindingType) { errorListener(new Error(`Invalid term type '${currentBindingType}' on line ${parser.line + 1}`)); } if (term) { if (currentQuotedTriples.length > 0) { // If we're in a quoted triple, store the term inside the active quoted triple const currentQuotedTriple = currentQuotedTriples[currentQuotedTriples.length - 1]; if (currentQuotedTriple.components[currentQuotedTriple.currentComponent]) { errorListener(new Error(`The ${currentQuotedTriple.currentComponent} in a quoted triple on line ${parser.line + 1} was already defined before`)); } currentQuotedTriple.components[currentQuotedTriple.currentComponent] = term; } else { // Store the value in the current bindings object const key = this.prefixVariableQuestionMark ? ('?' + currentBindingName) : currentBindingName; currentBindings[key] = term; } } currentBindingType = undefined; } stack.pop(); }); parser.on("text", text => { if (this.stackBeginsWith(stack, ['sparql', 'results', 'result', 'binding']) && stack[stack.length - 1] === currentBindingType) { currentText = text; } }); const resultStream = sparqlResponseStream .on("end", _ => { if (!resultsFound) { resultStream.emit("error", new Error("No valid SPARQL query results were found.")); } else if (!variablesFound) { resultStream.emit('variables', []); } }) .pipe(new readable_stream_1.Transform({ objectMode: true, transform(chunk, encoding, callback) { parser.write(chunk); callback(); } })); if (version && !this.isValidVersion(version)) { resultStream.destroy(new Error(`Detected unsupported version as media type parameter: ${version}`)); } return resultStream; } /** * Convert a SPARQL XML boolean response stream to a promise resolving to a boolean. * This will reject if the given response was not a valid boolean response. * @param {NodeJS.ReadableStream} sparqlResponseStream A SPARQL XML response stream. * @param version The version that was supplied as a media type parameter * @return {Promise<boolean>} The response boolean. */ parseXmlBooleanStream(sparqlResponseStream, version) { if (version && !this.isValidVersion(version)) { return Promise.reject(new Error(`Detected unsupported version as media type parameter: ${version}`)); } return new Promise((resolve, reject) => { const parser = new saxes_1.SaxesParser(); const stack = []; parser.on("error", reject); parser.on("opentag", tag => { stack.push(tag.name); }); parser.on("closetag", _ => { stack.pop(); }); parser.on("text", text => { if (this.stackEquals(stack, ['sparql', 'boolean'])) { resolve(text === 'true'); } }); sparqlResponseStream .on('error', reject) .on('data', d => parser.write(d)) .on('end', () => reject(new Error('No valid ASK response was found.'))); }); } stackEquals(a, b) { return a.length === b.length && a.every((v, i) => b[i] === v); } stackBeginsWith(a, b) { return a.length >= b.length && b.every((v, i) => a[i] === v); } } exports.SparqlXmlParser = SparqlXmlParser; SparqlXmlParser.SUPPORTED_VERSIONS = [ '1.2', '1.2-basic', '1.1', ]; //# sourceMappingURL=SparqlXmlParser.js.map