This document is licensed under a Creative Commons Attribution 3.0 License.
In the Resource Description Framework, literals are composed of a UNICODE string (the lexical form), a datatype IRI, and optionally, when the datatype IRI is rdf:langString, a language tag. Any IRI can take the place of a datatype IRI, but the specification only defines the precise meaning of a literal when the datatype IRI is among a predefined subset. Custom datatypes have reported use on the web of data, but their support by RDF processors is rare and implementation specific.
We propose a mechanism for a generic support of custom datatypes.
Following simple guidelines, (i) definitions of arbitrary custom datatypes may be published on the web, and (ii) a generic RDF processor or SPARQL query engine can discover datatypes on-the-fly, and perform operations uniformly.
This document provides:
This document is merely a public working draft of a potential specification. It has no official standing of any kind and does not represent the support or consensus of any standards organisation.
CustomDatatypeFactoryCustomDatatypeCustomDatatypeCustomDatatypeCustomDatatypeCustomDatatypeAs well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MUST and SHOULD are to be interpreted as described in [RFC2119].
Custom Datatypes MUST conform to these guidelines.
RDF processors and SPARQL query engines SHOULD take care of the code they process.
Recommendations for Custom Datatype Publishers are as follows.
aaa to identify custom datatype Da.aaa, let it retrieve a document that contains executable code.CustomDatatypeFactory, an interface with a unique function getDatatype( iri ), whose behaviour is described below.aaa, function getDatatype( iri ) returns an object that implements interface CustomDatatype described below.This API provides mechanisms that enable developers to process custom datatypes and custom literals uniformly.
CustomDatatypeFactoryInterface CustomDatatypeFactory MUST implement the following methods.
[Constructor]
interface CustomDatatypeFactory {
CustomDatatype getDatatype (String iri);
};getDatatypeIn the context of the code contained in a custom datatype specification file, i.e., an instance of interface CustomDatatypeFactory, getDatatype(uri) returns an instance of interface CustomDatatype, or throws an exception.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| iri | String | ✘ | ✘ |
CustomDatatypeCustomDatatypeInterface CustomDatatype MUST implement the following methods.
[Constructor]
interface CustomDatatype {
String getIri ();
Boolean isWellFormed (String lexicalForm);
Boolean recognisesDatatype (String datatypeIri);
String[] getRecognisedDatatypes ();
Boolean isEqual (String lexicalForm1, String lexicalForm2, optional String datatypeIri2);
Integer compare (String lexicalForm1, String lexicalForm2, optional String datatypeIri2);
String getNormalForm (String lexicalForm);
String importLiteral (String lexicalForm, String datatypeIri);
String exportLiteral (String lexicalForm, String datatypeIri);
};comparelexicalForm and this datatype is lower, equal, or greater than the value of literal with lexical form lexicalForm2 and datatype identified by IRI datatypeIri2.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| lexicalForm1 | String | ✘ | ✘ | |
| lexicalForm2 | String | ✘ | ✘ | |
| datatypeIri2 | String | ✘ | ✔ |
IntegerexportLiteraldatatypeIri, with a value equal to that of a literal with lexical form lexicalForm and this datatype.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| lexicalForm | String | ✘ | ✘ | |
| datatypeIri | String | ✘ | ✘ |
StringgetIriStringgetNormalFormlexicalForm and this datatype.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| lexicalForm | String | ✘ | ✘ |
StringgetRecognisedDatatypesString[]importLiterallexicalForm and datatype identified by datatypeIri.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| lexicalForm | String | ✘ | ✘ | |
| datatypeIri | String | ✘ | ✘ |
StringisEquallexicalForm1 and this datatype has the same value as literal with lexical form lexicalForm2 and datatype identified by IRI datatypeIri2.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| lexicalForm1 | String | ✘ | ✘ | |
| lexicalForm2 | String | ✘ | ✘ | |
| datatypeIri2 | String | ✘ | ✔ |
BooleanisWellFormed| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| lexicalForm | String | ✘ | ✘ |
BooleanrecognisesDatatype| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| datatypeIri | String | ✘ | ✘ |
BooleanCustomDatatypeLet da be the specification object of a custom datatype Da identified by IRI aaa, i.e., an instance of interface CustomDatatype returned by a call to method getDatatype(aaa). da is intra-conformant if and only if all of the following is true
getIri MUST be such that:
da.getIri() returns a stringda.getIri() = aaaisWellFormed MUST be such that:
da.isWellFormed(sss) returns a boolean if sss is a string, and throws an exception otherwisegetNormalForm MUST be such that:
da.getNormalForm(sss) returns a string if da.isWellFormed(sss) = true, and throws an exception otherwiseda.isWellFormed(sss) = true, then da.isWellFormed( da.getNormalForm(sss) ) = trueda.isWellFormed(sss) = true, then da.getNormalForm( da.getNormalForm(sss) ) = da.getNormalForm(sss)recognisesDatatype MUST be such that:
da.recognisesDatatype(bbb) returns a boolean if bbb is a string, and throws an exception otherwiseda.recognisesDatatype(aaa) = truerecognisedDatatypes MUST be such that:
da.recognisedDatatypes() is the set of strings bbb such that da.recognisesDatatype(bbb) = trueimportLiteral MUST be such that:
da.recognisesDatatype(bbb) = true and ttt is a string, then da.importLiteral(ttt, bbb) either returns a string, or throws an exception. Else, it throws an exception.da.importLiteral(ttt, bbb) returns a string, then isWellFormed( da.importLiteral(ttt, bbb) ) = trueda.isWellFormed(sss) = true, then da.importLiteral(sss, aaa) returns a string, and da.getNormalForm(sss) = da.getNormalForm( da.importLiteral(sss, aaa) )exportLiteral MUST be such that:
da.recognisesDatatype(bbb) = true and da.isWellFormed(sss) = true, then da.exportLiteral(sss, bbb) either returns a string, or throws an exception. Else, it throws an exception.da.exportLiteral(sss, bbb) returns a string, then da.getNormalForm(sss) = da.getNormalForm( da.importLiteral( da.exportLiteral(sss, bbb), bbb) )isEqual MUST be such that:
da.isWellFormed(sss) = true and da.importLiteral(ttt, bbb) returns a string, then da.isEqual(sss, ttt, bbb) returns a boolean. Else, it throws an exception.da.isWellFormed(sss) = true and da.importLiteral(ttt, bbb) returns a string, then da.isEqual(sss, uuu, ccc) = true if and only if da.getNormalForm(sss) = da.getNormalForm( da.importLiteral(uuu, ccc) )da.isWellFormed(sss) = true and da.isWellFormed(ttt) = true, then da.isEqual(sss, ttt) = da.isEqual(sss, ttt, aaa). Else, it throws an exception.da.isWellFormed(sss) = true, da.isWellFormed(ttt) = true, and da.isWellFormed(uuu) = true, then:
da.isEqual(sss, ttt) = da.isEqual(ttt, sss)da.isEqual(sss, ttt) and da.isEqual(ttt, uuu), then da.isEqual(sss, uuu)compare MUST be such that:
da.isWellFormed(sss) = true and da.importLiteral(ttt, bbb) returns a string, then da.compare(sss, ttt, bbb) returns an integer. Else, it throws an exception.da.isWellFormed(sss) = true and da.importLiteral(ttt, bbb) returns a string, then da.compare(sss, uuu, ccc) = 0 if and only if da.isEqual(sss, uuu, ccc) = trueda.isWellFormed(sss) = true and da.isWellFormed(ttt) = true, then da.compare(sss, ttt) = da.compare(sss, ttt, aaa). Else, it throws an exception.da.isWellFormed(sss) = true, da.isWellFormed(ttt) = true, and da.isWellFormed(uuu) = true, then:
da.compare(sss, ttt) and da.compare(ttt, sss) have opposite signsda.compare(sss, ttt) ≤ 0 and da.compare(ttt, uuu) ≤ 0, then da.compare(sss, uuu) ≤ 0CustomDatatypeLet da be the specification object of a custom datatype Da identified by IRI aaa. da is extra-conformant if and only if, for every bbb in da.getRecognisedDatatypes(), all of the following is true.
importLiteral MUST be such that:
db.isWellFormed(ttt) = false, then da.importLiteral(ttt, bbb) throws an exception.exportLiteral MUST be such that:
da.exportLiteral(sss, bbb) returns a string, then db.isWellFormed( da.exportLiteral(sss, bbb) ).isEqual MUST be such that:
da.isEqual(sss, ttt, bbb) = true and db.isEqual(ttt, uuu, ccc) = true, then da.isEqual(sss, ttt, bbb) = true
compare MUST be such that:
da.compare(sss, ttt, bbb) ≤ 0 and db.compare(ttt, uuu, ccc) ≤ 0, then da.compare(sss, ttt, bbb) ≤ 0
da.compare(sss, ttt, bbb) ≥ 0 and db.compare(ttt, uuu, ccc) ≥ 0, then da.compare(sss, ttt, bbb) ≥ 0
CustomDatatypeLet da be the specification object of a custom datatype Da identified by IRI aaa. da is inter-conformant if and only if, for every bbb in da.getRecognisedDatatypes() such that one may retrieve a specification object db of the custom datatype Db identified by IRI bbb, then db is conformant, and all of the following is true.
importLiteral MUST be such that:
da.importLiteral(ttt, bbb) returns a string if and only if db.exportLiteral(ttt, aaa) returns a stringisEqual MUST be such that:
da.isEqual(sss, ttt, bbb) = true if and only if db.isEqual(ttt, sss, aaa) = true
compare MUST be such that:
da.importLiteral(sss, bbb) and db.exportLiteral(sss, aaa) returns a string, then da.compare(sss, ttt, bbb) and db.compare(ttt, sss, aaa) have opposite signsCustomDatatypeLet da be the specification object of a custom datatype Da identified by IRI aaa. da is conformant if and only if it is intra-, extra-, and inter-conformant altogether.
The implementation of Custom Datatype Length is available at URL http://w3id.org/lindt/v1/custom_datatypes
The implementation of Jena and ARQ with support for on-the-fly recognition of custom datatypes is available at URL https://github.com/thesmartenergy/jena
datasets files, are based on DBpedia 2014 English specific mapping-based properties dataset.
The test program is bundled in a Maven project DBpediaLengthQueries
The following are the [sparql11-query] requests that are evaluated on each dataset.
Dataset dbpedia PREFIX dbpdt: <http://dbpedia.org/datatype/>
SELECT ?x ?prop ?length ?metres WHERE {
VALUES (?factor ?unit)
{ (0.001 dbpdt:millimetre)
(0.01 <http://dbpedia.org/datatype/centimetre>)
(1 <http://dbpedia.org/datatype/metre>)
(1000 <http://dbpedia.org/datatype/kilometre>)
}
?x ?prop ?length .
BIND (?factor*<http://www.w3.org/2001/XMLSchema#decimal>(?length) as ?metres)
FILTER(datatype(?length) = ?unit)
FILTER( ?metres < 5 )
}
ORDER BY DESC ( ?metres )
LIMIT 100
Dataset custom
PREFIX cdt: <http://w3id.org/lindt/v1/custom_datatypes#>
SELECT ?x ?prop ?length WHERE {
?x ?prop ?length .
FILTER(datatype(?length) = cdt:length )
FILTER( ?length < "5m"^^cdt:length )
}
ORDER BY DESC (?length)
LIMIT 100
Dataset qudt
PREFIX qudt: <http://qudt.org/schema/qudt#>
PREFIX qudt-unit: <http://qudt.org/vocab/unit#>
SELECT ?x ?prop ?length (?factor*?length as ?metres) WHERE {
VALUES (?factor ?unit)
{ (0.001 qudt-unit:millimetre)
(0.01 qudt-unit:centimetre)
(1 qudt-unit:metre)
(1000 qudt-unit:kilometre)
}
?x ?prop [
qudt:quantityValue [
qudt:numericValue ?length ;
qudt:unit ?unit ] ] .
FILTER( ?factor*?length < 5 )
}
ORDER BY DESC (?metres)
LIMIT 100
lindt.dbpedialengthqueries.Main