Skip to content
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
14 changes: 14 additions & 0 deletions lib/RdfXmlParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ export class RdfXmlParser extends Transform implements RDF.Sink<EventEmitter, RD
* @return {NamedNode} an IRI.
*/
public valueToUri(value: string, activeTag: IActiveTag): RDF.NamedNode {
// Per RFC 3986, a URI scheme must be: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
// A forward slash is not allowed in a URI scheme, so a value like 'x/y:z' where a slash
// appears before the colon is unambiguously a relative IRI (not an absolute one).
// The relative-to-absolute-iri library incorrectly treats any value with a colon as absolute,
// so we must pre-process such relative IRIs containing a slash before the colon.
// We temporarily replace all colons with the null character U+0000 (which is illegal in XML
// and therefore can never legitimately appear in a parsed IRI value), resolve against the base
// IRI treating the whole value as a relative path, then restore the colons.
const colonPos = value.indexOf(':');
const firstSlashPos = value.indexOf('/');
if (colonPos > 0 && firstSlashPos >= 0 && firstSlashPos < colonPos) {
const encoded = value.split(':').join('\u0000');
return this.uriToNamedNode(resolve(encoded, activeTag.baseIRI).split('\u0000').join(':'));
}
return this.uriToNamedNode(resolve(value, activeTag.baseIRI));
}

Expand Down
30 changes: 30 additions & 0 deletions test/RdfXmlParser-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,21 @@ abc`)).rejects.toBeTruthy();
expect(parser.valueToUri('xyz', { baseIRI: 'http://aa/././a' }))
.toEqual(DF.namedNode('http://aa/xyz'));
});

it('create a named node from a relative IRI with a slash before the colon', () => {
expect(parser.valueToUri('x/y:z', { baseIRI: 'http://base.org/path/' }))
.toEqual(DF.namedNode('http://base.org/path/x/y:z'));
});

it('create a named node from a relative IRI with a slash before the colon and no trailing slash on base', () => {
expect(parser.valueToUri('x/y:z', { baseIRI: 'http://base.org/path' }))
.toEqual(DF.namedNode('http://base.org/x/y:z'));
});

it('create a named node from a relative IRI with multiple colons after a slash', () => {
expect(parser.valueToUri('x/y:z:w', { baseIRI: 'http://base.org/path/' }))
.toEqual(DF.namedNode('http://base.org/path/x/y:z:w'));
});
});

describe('should error with line numbers', () => {
Expand Down Expand Up @@ -2100,6 +2115,21 @@ abc`)).rejects.toBeTruthy();
]);
});

// 2.14 - relative IRI with a colon after a slash
it('relative IRI containing a slash before a colon should be resolved against the base IRI', async () => {
const parserThis = new RdfXmlParser({ baseIRI: 'https://example.com/base/' });
const array = await parse(parserThis, `<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:default="https://example.com/default/">
<default:C rdf:about="x/y:z"/>
</rdf:RDF>`);
return expect(array)
.toBeRdfIsomorphic([
quad('https://example.com/base/x/y:z', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
'https://example.com/default/C'),
]);
});

// 2.15
it('rdf:li properties', async () => {
const array = await parse(parser, `<?xml version="1.0"?>
Expand Down