This document discusses using JSON and JSON-LD to represent data as linked data. It begins with simple examples of representing a company and industry as JSON objects. It then shows how to add identifiers, types, and links to represent the relationships between entities and conform to linked data principles. Finally, it introduces the Linked Data Platform (LDP) specification and how to structure data and interactions according to its constraints and affordances to build fully RESTful and linked data compliant APIs.
6. media type
Content-Type and Media Type as defined in HTTPBIS
HTTP uses Internet Media Types [RFC2046] in the Content-Type
(Section 3.1.1.5) [...] header fields in order to provide open and
extensible data typing [...]. Media types define both a data format and
various processing models.
6 / 46
7. IANA registry for media types
IANA registry for Media Types
Name Template Reference
json application/json [RFC7158]
7 / 46
8. JSON
The JavaScript Object Notation (JSON) Data Interchange Format [RFC7159]
JSON can represent four primitive types (strings, numbers, booleans,
and null) and two structured types (objects and arrays).
8 / 46
9. JSON
The JavaScript Object Notation (JSON) Data Interchange Format [RFC7159]
JSON can represent four primitive types (strings, numbers, booleans,
and null) and two structured types (objects and arrays).
No processing model defined in JSON! (neither in JSON-LD, HAL,
etc.)
9 / 46
10. extending media type semantics
what wouldthe RESTafarian do?
probably use a vendor specific media type
$HEADhttps://data.example.com/company/
Content-Type:application/vnd.example.company+json
10 / 46
11. Linkheader
rel=profile Link header
The 'profile' link relation type [...] allows resource representations to
indicate that they are following one or more profiles. A profile is
defined not to alter the semantics of the resource representation
itself, but to allow clients to learn about additional semantics [...] in
addition to those defined by the media type [...].
$HEADhttps://data.example.com/company/
Content-Type:application/json
Link:<https://data.example.com/ontology#Company>;rel="profile"
11 / 46
12. HATEOAS?
Hypermedia can be the engine of a single API.
Hypermedia cannot be the engine of cross-API interactions.
Ruben Verborgh, Hypermedia Cannot be the Engine
12 / 46
13. focusing on data
HATEOAS is like a hypermedia state machine
data modeling is a special case for application state
media type encodes kind/type
but few missing interactions eg. CRUD
13 / 46
14. data patterns in JSON
encodings
links
types
disambiguating schemas
Let's make JSON a hypermedia format :-)
14 / 46
15. a 1st version
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"id":"q6po09",
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"id":"c34dap",
"name":"Apple",
"ticker":"AAPL",
"industry":"q6po09"
}
documentation probably available at something like
https://data.example.com/api/documentation 15 / 46
16. things, not String-s
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"@id":"https://data.example.com/industry/q6po09",
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"@id":"https://data.example.com/company/c34dap",
"name":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
16 / 46
17. this is its own @id
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"name":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
17 / 46
18. what are we dealing with?
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"type":"Industry",
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"type":"Company",
"name":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
18 / 46
19. @type+ URL
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"@type":{"@id":"https://data.example.com/ontology#Industry"},
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"@type":{"@id":"https://data.example.com/ontology#Company"},
"name":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
19 / 46
20. disambiguating attributes [1/3]
How to distinguish an industry's name from a company's name?
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"@type":{"@id":"https://data.example.com/ontology#Industry"},
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"@type":{"@id":"https://data.example.com/ontology#Company"},
"name":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
20 / 46
21. disambiguating attributes [2/3]
How to distinguish an industry's name from a company's name?
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/json
{
"@type":{"@id":"https://data.example.com/ontology#Industry"},
"https://data.example.com/ontology#industryName":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"@type":{"@id":"https://data.example.com/ontology#Company"},
"https://data.example.com/ontology#companyName":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
21 / 46
22. disambiguating attributes [3/3]
How to distinguish an industry's name from a company's name?
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"@context":{
"name":{"@id":"https://data.example.com/ontology#companyName"}
},
"@type":{"@id":"https://data.example.com/ontology#Company"},
"name":"Apple",
"ticker":"AAPL",
"industry":{"@id":"https://data.example.com/industry/q6po09"}
}
22 / 46
28. linking vs embedding
an Industry entity
$GEThttps://data.example.com/industry/q6po09
Content-Type:application/ld+json
{
"@context":{...},
"@type":"Industry",
"name":"Fruits"
}
a Company entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/ld+json
{
"@context":{...},
"@type":"Company",
"name":"Apple",
"ticker":"AAPL",
"industry":"https://data.example.com/industry/q6po09"
}
28 / 46
29. linking vs embedding
a Company entity with its Industry entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/ld+json
{
"@context":{...},
"@type":"Company",
"name":"Apple",
"ticker":"AAPL",
"industry":{
"@type":"Industry",
"name":"Fruits"
}
}
29 / 46
30. deep linking
a Company entity with its Industry entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/ld+json
{
"@context":{...},
"@type":"Company",
"name":"Apple",
"ticker":"AAPL",
"industry":{
"@id":"#q6po09",
"@type":"Industry",
"name":"Fruits"
}
}
Now I could use <https://data.example.com/company/c34dap#q6po09>to speak
about the Industryreferenced from the document defining it.
30 / 46
31. inlining
a Company entity inlining the linked Industry entity
$GEThttps://data.example.com/company/c34dap
Content-Type:application/ld+json
{
"@context":{...},
"@type":"Company",
"name":"Apple",
"ticker":"AAPL",
"industry":{
"@id":"https://data.example.com/industry/q6po09",
"@type":"Industry",
"name":"Fruits"
}
}
remarks
all (or part of) <https://data.example.com/industry/q6po09>'s state is
inlined
the Industry's identity+state is not under this Company's control
could even live on a different domain!
applications could decide to follow links to get authoritative state
31 / 46
32. Summary so far
started with
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"id":"c34dap",
"name":"Apple",
"ticker":"AAPL",
"industry":"q6po09"
}
32 / 46
33. Summary so far
started with
$GEThttps://data.example.com/company/c34dap
Content-Type:application/json
{
"id":"c34dap",
"name":"Apple",
"ticker":"AAPL",
"industry":"q6po09"
}
ended up with something like
$GEThttps://data.example.com/company/c34dap
Content-Type:application/ld+json
{
"@context":{...},
"@type":"Company",
"name":"Apple",
"ticker":"AAPL",
"industry":"https://data.example.com/industry/q6po09"
}
33 / 46
34. Immediate benefits
entities are referenced, embedded and linked together
we get for free provenance and data authority
attributes are disambiguated and can be interrogated
values are typed and can natively be links
vocabularies can be shared, reused and even checked
34 / 46
35. introducing LDP
Linked Data Platform
intersection of REST and RDF
being specced at W3C for the past 3 years
state-of-the-art of REST APIs
35 / 46
38. enabling LDP interaction model
rel=type Link header
The "type" link relation can be used to indicate that the context
resource is an instance of the resource identified by the target
Internationalized Resource Identifier (IRI).
in LDP
The presence of this header asserts that the server complies with the
LDP specification's constraints on HTTP interactions with LDPRs, that
is it asserts that the resource has Etags, has an RDF representation,
and so on, which is not true of all Web resources served as RDF media
types.
$HEADhttps://data.example.com/company/
Content-Type:application/ld+json
Link:<http://www.w3.org/ns/ldp#BasicContainer>;rel="type"
38 / 46
39. LDP interaction model
resources
Resource (LDPR) ← RDF Source (LDPRS) ← Container (LDPC) ← Basic
Container (Basic Container)
affordance
advertised through rel=typeLink header, not Content-Type
a GET on a container (LDPC) returns its state (ie. its members)
resources (LDPR) are created by POSTing to the container
HTTP 201 Created + Locationheader
use DELETE to remove a member
use PUT to override the state of a member
use PATCH for partial updates
39 / 46
40. LDP Paging (Working Draft) [1/2]
HTTP 303 See Other
A 303 response to a GET request indicates that the origin server does
not have a representation of the target resource [...] the Location field
value refers to a resource that is descriptive of the target resource
also
server has control over the pagination mechanism
client sets preferences
semantics: snapshot, consistency, etc.
40 / 46
41. LDP Paging (Working Draft) [2/2]
use Link relations eg. rel='next'
$GEThttps://data.example.com/company/
303SeeOther
Location:https://data.example.com/company/?p=5YjF
$GEThttps://data.example.com/company/?p=5YjF
200OK
Link:<https://data.example.com/company/?p=AyOW>;rel="next"
Content-Type:application/ld+json
{...}
Save a round-trip: HTTP 333 (being standardised)
41 / 46
43. ad-hoc service-oriented API [1/2]
$GEThttps://data.example.com/api/getCompaniesByIndustry?industryName="Fruits"
Content-Type:application/ld+json
{
"@context":{...},
"results":[
{"@id":"https://data.example.com/company/c34dap",...},
...
]
}
domain aware
more capabilities
requires to read documentation
return resource URIs and use inlining when needed
43 / 46
44. ad-hoc service-oriented API [2/2]
microservices
going further
general purpose service-oriented API?
analytics
44 / 46
45. error handling
LDP servers MUST publish any constraints on LDP clients’ ability to
create or update LDPRs, by adding a Link header with
rel='describedby' [RFC5988] to all responses to requests which fail due
to violation of those constraints. [LDP 4.2.1.2]
so basically, HTTP 4xx + rel='describedby'Link header
use HTTP 400 for application-specific error [LDP ISSUE-98]
for machine readable errors, consider Problem Details for HTTP APIs [http-
problem-06]
45 / 46
46. to read
HTTP/REST
Web Client Programming with Perl
http://www.slideshare.net/RubenVerborgh/hypermedia-cannot-be-the-
engine
LDP
LDP 1.0 Primer (Editor's Draft)
LDP 1.0 (Editor's Draft)
RDF
http://www.w3.org/TR/2014/REC-turtle-20140225/
http://semanticweb.com/introduction-to-rdf_b17953
http://www.w3.org/TR/2012/REC-rdb-direct-mapping-20120927/
46 / 46