Internet-Draft | Designing APIs with REST API Linked Data | October 2024 |
Polli | Expires 11 April 2025 | [Page] |
This document provides guidance for designing schemas using REST API Linked Data keywords.¶
This note is to be removed before publishing as an RFC.¶
Status information for this document may be found at https://datatracker.ietf.org/doc/draft-polli-design-process/.¶
information can be found at https://github.com/ioggstream/draft-polli-restapi-ld-keywords.¶
Source for this draft and an issue tracker can be found at https://github.com/ioggstream/draft-polli-restapi-ld-keywords/issues.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 11 April 2025.¶
Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
This document provides guidance and examples for designing schemas using REST API Linked Data keywords.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. These words may also appear in this document in lower case as plain English words, absent their normative meanings.¶
All JSON examples are represented in YAML format for readability and conciseness.¶
The term "schema instance" referso to a JSON document that conforms to a JSON Schema.¶
There are different ways to model a vocabulary-based entry, e.g., a list of countries or a list of currencies.¶
Normally, you would use a JSON Schema (e.g., with an enum
keyword):¶
The resulting schema instance is a simple string
(e.g. ITA
),
while JSON-LD only supports JSON objects (in compact form)
or JSON arrays (in expanded forms).
Please note that the expanded form is not supported by
[I-D.polli-restapi-ld-keywords].¶
To be able to represent the entry in JSON-LD, an enumerated entry can be modeled using a specific property for the identifier, and a JSON-LD context.¶
Country: type: object properties: identifier: $ref: "#/components/schemas/CountryCode" name: type: string example: identifier: ITA name: Italy¶
Linked Data keywords provide a context. Different contexts can lead to different RDF representations for the same schema instances (i.e. the actual data).¶
Isomorphic representation: the RDF representation preserves the structure of the JSON object.¶
CountryBlankNode: x-jsonld-type: Country x-jsonld-context: "@vocab": "https://schema.org/" type: object properties: identifier: "$ref": "#/components/schemas/CountryCode" name: type: string example: identifier: ITA name: Italy¶
results in the following RDF graph using a blank node:¶
Non-isomorphic representation: one property maps to the node name.¶
Associating a property with the @id
keyword and a @base
prefix,
we state that the corresponding value is the name of the node.
This schema¶
CountryURI: x-jsonld-type: Country x-jsonld-context: "@vocab": "https://schema.org/" identifier: "@id" "@base": "https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3#" type: object properties: identifier: $ref: "#/components/schemas/CountryCode" name: type: string example: identifier: ITA name: Italy¶
results in the following RDF graph using a named node:¶
When modeling an object with references, the parent's context will normally provide the context for the child.¶
The following example models a Person
object with a nationality
property
referencing the CountryCode
schema.
The x-jsonld-context ensures that the nationality
property will be resolved to an URI,
though there is no space in the schema instance to provide a name for the country.¶
Person: x-jsonld-type: Person x-jsonld-context: "@vocab": "https://schema.org/" nationality: "@type": "@id" "@context": "@base": "https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3#" type: object properties: givenName: type: string familyName: type: string nationality: $ref: "#/components/schemas/CountryCode" example: givenName: John familyName: Doe nationality: ITA¶
results in the following RDF graph:¶
To provide a label or other properties for the country, we can use a nested object.¶
NestedPerson: x-jsonld-type: Person x-jsonld-context: "@vocab": "https://schema.org/" type: object properties: givenName: type: string familyName: type: string nationality: $ref: "#/components/schemas/CountryURI" example: givenName: John familyName: Doe nationality: identifier: ITA name: Italy¶
An implementation supporting context composition
will check that the value of NestedPerson/x-jsonld-context/nationality/@context
is undefined,
and will then integrate the information present in CountryURI/x-jsonld-context
into the instance context.¶
results in the following RDF graph:¶
To minimize context information, a common practice is to name JSON Schema properties after the corresponding RDF predicates.¶
As we can see from the above schema, this can lead to inheriting
non uniform naming conventions from the RDF vocabulary:
for example, birthPlace
and hasOccupation
both target objects,
while only hasOccupation
starts with a verb (i.e. has
).¶
Another issue is related to the schema instance size when using very long property or class names such as https://schema.org/isAccessibleForFree and https://schema.org/IPTCDigitalSourceEnumeration.¶
Mapping JSON Schema properties to RDF predicates in x-jsonld-context can reduce semantic risks when some ontologies changes, or when there's a need to switch to a different ontology: this is because having different names for the property and the predicate clarifies that the property may well evolve into a different predicate in time, like shown in the following example.¶
Instead of using a generic surname
, this schema uses
the more specific patronymicName
named after the corresponding RDF predicate.¶
Person: x-jsonld-context: "@vocab": "http://w3.org/ns/person#" properties: patronymicName: type: string example: patronymicName: "Ericsson" x-rdf: >- _:b0 :patronymicName "Ericsson" .¶
If the service evolves to be more generic (e.g., moving to foaf:
),
the property name might be mapped
to the foaf:familyName
predicate, but the schema instance will remain the same
thus retaining the information of a legacy ontology.¶
A more flexible design would have been to use a generic surname
property name,
and either map it to http://w3.org/ns/person#patronymicName
or foaf:familyName
in the context.¶
Always prefer explicit context information over implicit context composition. Different implementations of context composition may lead to different results, especially over large schemas with many nested objects.¶
While composition is useful in the schema design phase, bundling and validating the composed context in the final schema definition reduces the risk of interoperability issues.¶
The following example shows a Person JSON Schema with semantic information provided by the x-jsonld-type and x-jsonld-context.¶
The example object is assembled as a JSON-LD object as follows.¶
{ "@context": { "@vocab": "https://schema.org/", "custom_id": null }, "@type": "https://schema.org/Person", "familyName": "Doe", "givenName": "John", "country": "FRA", "custom_id": "12345" }¶
The above JSON-LD can be represented as text/turtle
as follows.¶
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> @prefix schema: <https://schema.org/> _:b0 rdf:type schema:Person ; schema:country "FRA" ; schema:familyName "Doe" ; schema:givenName "John" .¶
The following example shows a "Person" schema with semantic information provided by the x-jsonld-type and x-jsonld-context.¶
The resulting RDF graph is¶
The following schema contains a cyclic reference.¶
Person: description: Simple cyclic example. x-jsonld-type: Person x-jsonld-context: "email": "@id" "@vocab": "https://w3.org/ns/person#" children: "@container": "@set" type: object properties: email: { type: string } children: type: array items: $ref: '#/Person' example: email: "mailto:a@example" children: - email: "mailto:dough@example" - email: "mailto:son@example"¶
The example schema instance contained in the above schema results in the following JSON-LD document.¶
{ "email": "mailto:a@example", "children": [ { "email": "mailto:dough@example", "@type": "Person" }, { "email": "mailto:son@example", "@type": "Person" } ], "@type": "Person", "@context": { "email": "@id", "@vocab": "https://w3.org/ns/person#", "children": { "@container": "@set" } } }¶
Applying the workflow described in Section 1.5 just recursively copying the x-jsonld-context, the instance context could have been more complex.¶
In the following schema document, the "Citizen" schema references the "BirthPlace" schema.¶
The example schema instance contained in the above schema results in the following JSON-LD document. The instance context contains information from both "Citizen" and "BirthPlace" semantic keywords.¶
That can be serialized as text/turtle
as¶
Thanks to Giorgia Lodi, Matteo Fortini and Saverio Pulizzi for being the initial contributors of this work.¶
In addition to the people above, this document owes a lot to the extensive discussion inside and outside the workgroup. The following contributors have helped improve this specification by opening pull requests, reporting bugs, asking smart questions, drafting or reviewing text, and evaluating open issues:¶
Pierre-Antoine Champin, and Vladimir Alexiev.¶
This section is to be removed before publishing as an RFC.¶
TBD¶