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[].hreffor 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
keyof the_linksobject is the name of the relation. - Collection+JSON and Siren: the
linksproperty is always an array and the relation is defined asrel-property of the link item - Mason: the
keyof the@linksobject is the name of the relation. - jsonapi: the
keyof thelinksobject 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
hrefof 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
idandtypeproperty (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
_linksand_embeddedare reserved words: everything else is a plain old javascript object - Collection+JSON: Every item has to be in a
collection.itemsarray. The item has adataarray. These data items have anameand avalueproperty. - 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,idandlinks) - Mason: Only the
@-prefixed properties like@linksare reserved. Everything else is a plain json object. - Siren: Properties are stored as key value in a special
propertiesproperty. - UBER: The generic
datakey contain data items with anameand avalueproperty.
Actions
Defining actions
- HAL: Actions and Links are the same thing in HAL and are stored in
_linksobject. - Collection+JSON: Has a special
templateobject, which defines which fields to send on POST/PUT. - Hydra/JSON-LD: Within the
hydra:Linkproperty there is a specialsupportedOperationobject. - jsonapi: As far as I can see, jsonapi treats actions and links the same (like HAL).
- Mason: Has a special
@actionsproperty, which is similar to the@linksproperty. - Siren: Has a special
actionsproperty as an array, which is similar to thelinksproperty. - UBER: The data item may have a
actionproperty, 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
templateobject. - Hydra/JSON-LD: The possible operations (e.g.
POST) are defined in thehydra:supportedOperationobject. - jsonapi: As far as I can see, jsonapi does not describe the possible methods on a linked resource.
- Mason: The
@actionsitems have extra properties likeschemaUrlortypeto indicate what should be send to this target. Usually POST is expected as request method. - Siren: The
actionsitems have an additionalmethodproperty, which contains the HTTP method (e.g."POST"). - UBER: The
actionproperty 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.