The representation that is finally returned for a request is determined by a Formatter.
An API may have a number of formatters available for generating responses for a request. Clients may choose from amongst these formatters using content negotiation.
This specification defines five types of "built-in" Formatters that produce outputs in a range of different serialisations.
Conforming implementations MUST support the RDF and JSON formatters and SHOULD support the additional formatters in order to provide clients with additional flexibility in quering an API.
The XSLT Formatter provides a built-in extension point for supporting a range of additional serializations through transformation of the simple XML format.
The following table identifies the five built in formatters. Each formatter is listed along with its name, its URI, the mimetype it generates, and whether implementations are required to support it:
Description | Name | URI | Mimetype | Required? |
---|---|---|---|---|
RDF Formatter | rdf |
api:RdfXmlFormatter |
application/rdf+xml |
Yes |
JSON Formatter | json |
api:JsonFormatter |
application/json |
Yes |
Turtle Formatter | ttl |
api:TurtleFormatter |
text/turtle |
No |
XML Formatter | xml |
api:XmlFormatter |
application/xml |
No |
XSLT Formatter | N/A | api:XsltFormatter |
N/A | No |
The XSLT Formatter does not specify a name or a mimetype. Instances of that formatter are intended to be configured with a name and mimetype appropriate to the XSLT stylesheet used to serialize the results.
Implementations may support other formatter types, or support additional configuration of the built-in formatter types through implementation-specific properties as long as their basic behaviours remain unchanged.
For a request to an endpoint, the list of possible formatters is constructed from:
- the built-in formatters supported by the API implementation
- the
api:defaultFormatter
property on the API - the
api:formatter
property on the API - the
api:defaultFormatter
property on the selected endpoint - the
api:formatter
property on the selected endpoint
Two formatters may have the same name, indicated by the api:name
property. A formatter lower in this list overrides the one from higher in the list.
For example a 'json'
formatter defined on an Endpoint will override the built-in 'json'
formatter.
The formatter that is to be used when producing results from a request is determined as follows:
- if the
_format
request parameter is specified, and the API'sapi:contentNegotiation
property isapi:parameterBased
, the formatter named in that parameter. If there is no such formatter, a400 Bad Request
response is given. - otherwise, if the last path segment in the request URI uses an extension, such as
.ttl
, the formatter name is the one given after the final dot in that path segment. If there is no such formatter, continue (note that in this case the suffix will not be stripped from the request path prior to matching against endpoint URIs). - otherwise, use the
Accept
header in the request to perform content negotiation based on theapi:mimeType
properties of the available formatters - otherwise, if the endpoint contains a
api:defaultFormatter
, use that - otherwise, if the API contains a
api:defaultFormatter
, use that - otherwise use the
api:jsonFormatter
A Formatter component is passed the following logical parameters when executed:
- the RDF graph constructed by the viewer
- the URI of the root resource in the graph: in the case of an item endpoint, this is the item itself; in the case of a list endpoint, it is the current page
- the variable bindings, as described above
- the prefix bindings present within the configuration file
Implementations are free to pass these parameters to the formatter in whatever manner is best suited. Additional parameters or context can also be made available.
RDF/XML formatters should create RDF/XML.
They should use the prefix bindings that they are passed as namespace declarations within the RDF/XML.
The "Viewing Resources" section of the specification includes a worked example for a list endpoint that uses the Turtle syntax. Continuing that example, an RDF/XML Formatter would serialize that graph as RDF/XML, applying the available prefix bindings.
The following document illustrates on possible serialization of that example graph:
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
xmlns:api="http://purl.org/linked-data/api/vocab#"
xmlns:dct="http://purl.org/dc/terms/"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:xhv="http://www.w3.org/1999/xhtml/vocab#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<api:List rdf:about="http://api.example.org/people">
<dct:hasPart rdf:resource="http://api.example.org/people?_page=0"/>
<api:definition rdf:resource="http://api.example.org/spec/people"/>
<rdfs:label xml:lang="en">List of people</rdfs:label>
</api:List>
<api:Page rdf:about="http://api.example.org/people?_page=0">
<opensearch:itemsPerPage rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">10</opensearch:itemsPerPage>
<opensearch:startIndex rdf:datatype="http://www.w3.org/2001/XMLSchema#integer">1</opensearch:startIndex>
<dct:hasFormat rdf:resource="http://api.example.org/people.json?_page=0&_view=default"/>
<dct:hasFormat rdf:resource="http://api.example.org/people.rdf?_page=0&_view=default"/>
<dct:hasFormat rdf:resource="http://api.example.org/people.ttl?_page=0&_view=default"/>
<dct:isPartOf rdf:resource="http://api.example.org/people"/>
<api:items rdf:parseType="Collection">
<rdf:Description rdf:resource="http://people.example.org/bob"/>
<rdf:Description rdf:resource="http://people.example.org/mary"/>
</api:items>
<xhv:next rdf:resource="http://api.example.org/people?_page=1"/>
<rdfs:label xml:lang="en">First page of the list of people</rdfs:label>
<owl:sameAs rdf:resource="http://api.example.org/people?_page=0&_view=default"/>
</api:Page>
<foaf:Person rdf:about="http://people.example.org/bob">
<foaf:name>Bob</foaf:name>
</foaf:Person>
<foaf:Person rdf:about="http://people.example.org/mary">
<foaf:name>Mary</foaf:name>
</foaf:Person>
<rdf:Description rdf:about="http://api.example.org/people.json?_page=0&_view=default">
<dct:format>
<rdf:Description>
<rdfs:label>application/json</rdfs:label>
</rdf:Description>
</dct:format>
<dct:isFormatOf rdf:resource="http://api.example.org/people?_page=0&_view=default"/>
<rdfs:label xml:lang="en">JSON format of the default view of the first page of the list of people</rdfs:label>
</rdf:Description>
<rdf:Description rdf:about="http://api.example.org/people.rdf?_page=0&_view=default">
<dct:format>
<rdf:Description>
<rdfs:label>application/rdf+xml</rdfs:label>
</rdf:Description>
</dct:format>
<dct:isFormatOf rdf:resource="http://api.example.org/people?_page=0&_view=default"/>
<rdfs:label xml:lang="en">RDF/XML format of the default view of the first page of the list of people</rdfs:label>
</rdf:Description>
<rdf:Description rdf:about="http://api.example.org/people.ttl?_page=0&_view=default">
<dct:format>
<rdf:Description>
<rdfs:label>text/turtle</rdfs:label>
</rdf:Description>
</dct:format>
<dct:isFormatOf rdf:resource="http://api.example.org/people?_page=0&_view=default"/>
<xhv:next rdf:resource="http://api.example.org/people.ttl?_page=1&_view=default"/>
<rdfs:label xml:lang="en">Turtle format of the default view of the first page of the list of people</rdfs:label>
</rdf:Description>
</rdf:RDF>
Turtle formatters should create Turtle.
They should use the prefix bindings that they are passed as prefix declarations within the Turtle.
The "Viewing Resources" section of the specification includes a worked example for a list endpoint that uses the Turtle syntax. Continuing that example, a Turtle Formatter would serialize that graph, applying the available prefix bindings.
The JSON formatter creates a simple JSON format based on the RDF graph that it is provided.
The result is a single JSON object, that has the following basic structure:
{
"format": "linked-data-api" ;
"version": "0.2" ;
"result": {
...
}
}
The object has three properties:
format
: a fixed string that identifies this JSON structure as conforming to the JSON format described in this specificationversion
: a string version indicator tied to the version of this documentresult
: a JSON object that is the main entry point into the returned data.
When serialising into JSON the JSON Formatter should first identify the api:Page
resource in the provided RDF graph. This resource will be serialized as the value of the result
property in the JSON response.
After locating the resource the implementation should iterate through its properties, applying the following rules for serializing objects that are resources and literals.
This results in a traversal of the graph, touching each resource that is connected to the api:Page
resource. This will include
- the
api:List
(via thedct:isPartOf
property) - the selected items and any resources that they reference (via the
api:items
) property - the available alternate formats and versions (via
dct:hasFormat
anddct:hasVersion
properties)
Implementations should detect loops and for any resource that has already been visited in the graph, simply serialize its URI.
Any resources that are not reachable from the api:Page
resource, or resources to which it can be related, will not be included in the output.
The RDF properties of a resource are mapped onto JSON properties. The name of the JSON property is:
- the short name for the property, as described in the property paths section, if it has one
- the
rdfs:label
of the property, if it is a legal short name for a property that doesn't clash with an existing name - the local name of the property (the part after the last hash or slash), if it is a legal short name for a property that doesn't clash with an existing name
- the prefix associated with the namespace of the property (the part before the last hash or slash), concatenated with an underscore, concatenated with the local name of the property
RDF resources are either mapped onto JSON objects or simple string values depending on the amount of detail available about the resource in the graph being serialized.
The reserved property _about
is used to serialize the URI of a resource.
- if the resource has a URI, and is not the subject of any other statements in the graph then it is mapped onto a string whose value is the URI of the resource
- otherwise, if the resource has a URI, and is the subject of statements in the RDF graph, it is mapped onto a JSON object as described here. The object is given an
_about
property that contains the URI of the resource - otherwise, if the value is a blank node with no properties it is mapped onto a JSON object with no properties
- if the resource is a blank node that is the object or more than one statement within the graph, the object is given a
_id
property that contains a unique identifier for that blank node - otherwise, if the value is a
rdf:List
, it is mapped to an array whose items are the result of mapping the members of the list to JSON
Where possible literal values are mapped onto simple JSON literals or types.
The value of the JSON property is an array if the RDF property has more than one value in the RDF graph or if the api:multiValued
property of the RDF property has the value true
.
Each RDF literal is mapped onto a JSON value as follows:
- if the
api:structured
property of the RDF property has the valuetrue
then the value is represented as an object with a_value
property holding the value itself, and_lang
or_datatype
properties holding the language code and the short name of the datatype as applicable - otherwise, if the value has a datatype of
xsd:boolean
, it becomestrue
orfalse
- otherwise, if the value has a numeric datatype (one of the XML Schema numeric datatypes), it becomes a number in the JSON
- otherwise, if the value has the datatype
xsd:dateTime
it is mapped to a string in the date/time format recognised byDate.parse
, namelyEEE, d MMM yyyy HH:mm:ss 'GMT'Z
- otherwise, if the value has the datatype
xsd:date
it is mapped to a string in the date format recognised byDate.parse
, namelyyyyy-MM-dd
- otherwise, if the JSON property is an array (because the RDF property has multiple values or is marked as multi-valued) then the value is represented as a string; if the value has a language then
@{lang}
is appended to the string; if the value has a datatype then^^{datatype}
is appended to the string - otherwise (the value is a literal that isn't a boolean or number), it is mapped onto a string
Note: if a literal has a language, but is not marked as a being an api:structured
value, then it is mapped to a string, i.e. the language is lost in the serialization
Note: if the literal has a datatype, that is not an xsd:dateTime
, xsd:boolean
, or a number, but is not marked as being an api:structured
value, then it is mapped to a string.
Therefore this serialization of RDF to JSON is lossy. A trade-off is being made to provide a simple JSON format vs encompassing the full RDF model. Flexibility is provided to allow data publishers to preserve this data, as the cost of more complex JSON structures.
Implementations may provide a JSONP option, whereby, if the callback
request parameter is present, its value is a valid Javascript function name (matching the regular expression /^[a-zA-Z_][a-zA-Z0-9]*$/
), and the JSON Formatter is being used, the body of the response should consist of a line of Javascript calling the function named in the callback
parameter, with the JSON object as the argument of that function.
Eg:
If the request URI is:
/schools.json?callback=showSchoolsOnMap
The response body should be:
showSchoolsOnMap({})
({}
here, for the sake of brevity, represents the full JSON object, as described above, and detailed below. )
If the callback is not a valid function name, a 400 Bad Request
should be returned.
The "Viewing Resources" section of the specification includes a worked example for a list endpoint that uses the Turtle syntax.
The default JSON Formatter would serialize that graph into the following JSON document:
{
"format": "linked-data-api" ,
"version": "0.2" ,
"result": {
"_about": "http://api.example.org/people?_page=0",
"type": "http://purl.org/linked-data/api/vocab#Page",
"itemsPerPage": 10,
"startIndex": 1,
"items": [
{
"_about": "http://people.example.org/bob",
"type": "http://xmlns.com/foaf/0.1/Person",
"name": "Bob"
},
{
"_about": "http://people.example.org/mary",
"type": "http://xmlns.com/foaf/0.1/Person",
"name": "Mary"
}
],
"label": "First page of the list of people",
"isPartOf": {
"_about": "http://api.example.org/people",
"type": "http://purl.org/linked-data/api/vocab#List",
"label": "List of people",
"definition": "http://api.example.org/spec/people",
"hasPart": "http://api.example.org/people?_page=0"
},
"sameAs": "http://api.example.org/people?_page=0&_view=default",
"hasFormat": [
{
"_about": "http://api.example.org/people.ttl?_page=0&_view=default",
"label": "Turtle format of the default view of the first page of the list of people",
"format": {
"label": "text/turtle"
},
"isFormatOf": "http://api.example.org/people?_page=0&_view=default",
"next": "http://api.example.org/people.ttl?_page=1&_view=default"
},
{
"_about": "http://api.example.org/people.json?_page=0&_view=default",
"label": "JSON format of the default view of the first page of the list of people",
"format": {
"label":"application/json"
},
"isFormatOf": "http://api.example.org/people?_page=0&_view=default",
"next": "http://api.example.org/people.json?_page=1&_view=default"
},
{
"_about": "http://api.example.org/people.rdf?_page=0&_view=default",
"label": "RDF/XML format of the default view of the first page of the list of people",
"format": {
"label": "application/rdf+xml"
},
"isFormatOf": "http://api.example.org/people?_page=0&_view=default",
"next": "http://api.example.org/people.rdf?_page=1&_view=default"
}
],
"next": "http://api.example.org/people?_page=1"
}
}
The XML formatter creates an XML representation that is very similar to the JSON representation. The outermost object is a <result>
element with format
and version
attributes.
The resource described in the <result>
element is the entry point into the graph, as described above (the item for an item endpoint, the page for a list endpoint).
Resources are mapped onto XML elements as follows:
- if the resource is a blank node that is the object or more than one statement within the graph, the element is given a
id
attribute that contains a unique identifier for that blank node - otherwise, if the resource is not a blank node, the element is given an
href
attribute that contains the URI of the resource
The RDF properties of a resource are mapped onto XML elements. The name of the XML element is:
- the short name for the property, as described in the property paths section, if it has one
- the
rdfs:label
of the property, if it is a legal short name for a property that doesn't clash with an existing name - the local name of the property (the part after the last hash or slash), if it is a legal short name for a property that doesn't clash with an existing name
- the prefix associated with the namespace of the property (the part before the last hash or slash), concatenated with an underscore, concatenated with the local name of the property
The contents of the XML element is a sequence of <item>
elements if the RDF property has more than one value in the RDF graph or if the api:multiValued
property of the RDF property has the value true
.
Each RDF value is mapped onto some XML content as follows:
- if the value is a literal, it is mapped to a text node holding the value itself;
lang
ordatatype
attributes on the element hold the language code and the short name of the datatype as applicable - otherwise, if the value is a
rdf:List
, it is mapped to a sequence of<item>
elements, one representing each of the results of mapping the members of the list to XML - otherwise, if the value is a resource which is the subject of a statement in the RDF graph, it is mapped onto an XML element as described here
- otherwise, if the value is a blank node with no properties it is mapped onto an empty XML element (with an
id
attribute if it it referenced more than once) - otherwise, if the value is a resource the element is given an
href
attribute whose value is the URI of the resource
XSLT formatters create representations by taking the simple XML generated by the default XML formatter (api:xmlFormatter
) and transforming it using the stylesheet specified by the api:stylesheet
property of the XSLT formatter.
The XSLT stylesheet is passed an $api:namespaces
parameter which contains an XML document in the format:
<namespaces>
<namespace prefix="{prefix}">{value}</namespace>
... other namespaces ...
</namespaces>
It is also passed a parameter for each of the variable bindings, with the name equal to the variable name and the value being the string value of the variable.