In 2011 mnot wrote about json linking. Now it's like 4 years later and time to take a look at the link formats in json, which are used by lots of people.
In HTML5 we have a <link>
tag defined like this:
<link rel="author license" href="/about">
The attributes of this link are defined like this (taken from the W3C-page):
- href — Address of the hyperlink
- crossorigin — How the element handles crossorigin requests
- rel — Relationship between the document containing the hyperlink and the destination resource
- media — Applicable media
- hreflang — Language of the linked resource
- type — Hint for the type of the referenced resource
- sizes — Sizes of the icons (for rel="icon")
- Also, the title attribute has special semantics on this element: Title of the link; alternative style sheet set name.
The advantage of the <link>
tag in XML is, that you could put it anywhere in your document (like the <a>
tag in HTML).
If you want to achieve web linking (rfc5988) in JSON, there are multiple approaches.
This post shows some of the widely used JSON media types and how they deal with links. I will have a short look at HAL, Collection+JSON, Hydra/JSON-LD, Mason, Siren and UBER. Example files for all of them are in this json links gist.
Disclaimer: In the previous years I implemented HAL or several custom JSON based APIs. Most information in this post is gathered from the official pages of each media type. If you find some incorrect usage or better way to do something, please tell me!
Links vs. Actions
In an interesting thread on the hypermedia-web mailinglist Mike Amundsen explains that the links/queries/template in Collection+JSON have a special meaning:
items[].href
for parameterless read-only navigationlink[]
items for parameterless read-only navigationqueries[]
items for paramaterized read-only navigationtemplate{}
item is for paramaterized write navigations
Other media types (like e.g. HAL) don't make this distinction and use links for read and write navigation with parameters and without.
Some media types introduce a concept of actions
(Mason, Siren, UBER) or hydra:supportedOperation
in Hydra/JSON-LD.
In my understanding those actions are what template{}
is in Collection+JSON.
A special links
property
One approach is to use plain JSON (with your data) and add an extra _links
, @links
or links
property. Depending on
the approach this links
property contains an object (with relation as key of that object) or an array of multiple so
called link objects.
Handling the "rel" property
- HAL: the
key
of the_links
object is the name of the relation. - Collection+JSON and Siren: the
links
property is always an array and the relation is defined asrel
-property of the link item - Mason: the
key
of the@links
object is the name of the relation. - jsonapi: the
key
of thelinks
object is the name of the relation. - UBER: The
rel
-key of the link item is always an array of relations for the link
Inn UBER, Collection+JSON and Siren the rel is NOT the key of the object. This has the advantage, that one can put
multiple relations on one link (e.g. think of a first-page link, which is also a prev-page link if you are on the second
page). If you want to have multiple links to the same entity in e.g. HAL, you would define a _links
key first
and a
_links
key prev
, which contain the same reference.
Handling the "href" property
- Collection+JSON, HAL, Mason, Siren and UBER: the
href
of a link item is the link to the referenced resource. - jsonapi: the link item is a string which represents the href or a link object. The link object has an
id
andtype
property (see the docs)
Extra Type for Links in Hydra/JSON-LD
Hydra for JSON-LD uses a different approach. The href
is represented as string value of the target url (e.g.
"register_user": "/users/register"
and the @context
is used to describe the properties of the json. The hydra:Link
property (imported from http://www.w3.org/ns/hydra/core#), describes the link.
Special data
Property in UBER
In UBER, every content is placed in a data
array. That's why a link object can appear everywhere in the data
array. A
Link looks like this: {"rel" : ["self"], "url" : "http://example.org/"}
in UBER. So rel
and url
have a special meaning
in UBER's data objects.
Properties, which are not Links
- HAL: only
_links
and_embedded
are reserved words: everything else is a plain old javascript object - Collection+JSON: Every item has to be in a
collection.items
array. The item has adata
array. These data items have aname
and avalue
property. - Hydra/JSON-LD: Since JSON-LD defines the semantics within the
@context
- the rest of the response can be treated as a plain json object. - jsonapi: the special property
data
(next tolinks
) is always an array and contains plain json data (andtype
,id
andlinks
) - Mason: Only the
@
-prefixed properties like@links
are reserved. Everything else is a plain json object. - Siren: Properties are stored as key value in a special
properties
property. - UBER: The generic
data
key contain data items with aname
and avalue
property.
Actions
Defining actions
- HAL: Actions and Links are the same thing in HAL and are stored in
_links
object. - Collection+JSON: Has a special
template
object, which defines which fields to send on POST/PUT. - Hydra/JSON-LD: Within the
hydra:Link
property there is a specialsupportedOperation
object. - jsonapi: As far as I can see, jsonapi treats actions and links the same (like HAL).
- Mason: Has a special
@actions
property, which is similar to the@links
property. - Siren: Has a special
actions
property as an array, which is similar to thelinks
property. - UBER: The data item may have a
action
property, which indicates a possible an action
Describing allowed/expected HTTP methods
- HAL: By design it does not describe the possible methods on a linked resource.
- Collection+JSON: The method cannot be specified in the
template
object. - Hydra/JSON-LD: The possible operations (e.g.
POST
) are defined in thehydra:supportedOperation
object. - jsonapi: As far as I can see, jsonapi does not describe the possible methods on a linked resource.
- Mason: The
@actions
items have extra properties likeschemaUrl
ortype
to indicate what should be send to this target. Usually POST is expected as request method. - Siren: The
actions
items have an additionalmethod
property, which contains the HTTP method (e.g."POST"
). - UBER: The
action
property of the data item may contain "append", which is mapped to HTTP's POST
Conclusion
There have been approaches like JSON Reference, which defines
a link as { "$ref": "http://example.com/example.json#/foo/bar" }
, but if one compares that with the media types of this
post, it's basically very limited to have only a $ref
property defined :).
Even though URI templates have a wide acceptance as template language for query
strings, there are also some media type specific approaches to build HTML-Form-like behaviour in JSON. For example with HALO on top of HAL,
with Collection+JSON's prompt
property or with Siren's actions[n].fields[m].type
property to make generic browsers easier
to create.
Except in case of Siren's class
property, you need some thing like a Profile Link Relation Type (rfc6906)
to indicate of what type your returned entity is.
In my experience traversing the links does require a generic client library, especially if you write the M2M client
library for a service of someone else. For example even though it looks simple in the case of HAL, but you can fetch
href
of first linked entity in this way: response._links.item.href
OR response._links.item[0].href
. Which of
them is currently right, depends only on the server.
If one compares the media types (look at the examples at this json link gist) it's easy to see that there is no right or wrong way.
If you decide about using a media type, please checkout the great documentation for each of those media types and
evaluate the media types before you roll your own media type and stick to just application/json
!
PS: If you don't want to have links
in your JSON at all, the rfc5988 defines
also special Link
headers.