The application/microforms+json
data type is a notation for microforms that is strictly backwards compatible with JSON. You can also use application/microforms+jsonld
in case you want to carry JSON-LD.
By convention, hyperdata node keys are wrapped around a <>
to make them distinguishable from data, both visually as well as programatically.
For example, while this is data:
{
"a": "this is data",
}
Whereas this is hyperdata:
{
"<a>": "this is hyperdata"
}
Attributes can be assigned inside the JSON body
{
"<input>": {
"name": "bar"
}
}
Or at the key location as a list of key=value
pairs.
{
"<input name='foo'>": {},
}
The former is useful because it gives you the ability to write a list of elements that have the same name sequentially without creating an array. For example, this:
{
"<form>": {
"<input>": [{
"name": "foo",
"required": "true"
}, {
"name": "bar",
"required": "false"
}]
}
}
Can be written as:
{
"<form>": {
"<input name='foo'>": {
"required": "true"
},
"<input name='bar'>": {
"required": "false"
}
}
}
Alternatives considered
Wrapping
The most common strategy in hypermedia APIs (hal, hydra and siren) is to “wrap” the domain-specific data in containers:
{
"class": [ "order" ],
"properties": {
"orderNumber": 42,
"itemCount": 3,
"status": "pending"
},
"actions": [{
"name": "add-item",
"title": "Add Item",
"method": "POST",
"href": "http://api.x.io/orders/42/items",
"type": "application/x-www-form-urlencoded",
"fields": [
{ "name": "orderNumber", "type": "hidden", "value": "42" },
{ "name": "productCode", "type": "text" },
{ "name": "quantity", "type": "number" }
]
}],
"links": [
{ "rel": [ "self" ], "href": "http://api.x.io/orders/42" },
{ "rel": [ "previous" ], "href": "http://api.x.io/orders/41" },
{ "rel": [ "next" ], "href": "http://api.x.io/orders/43" }
]
}
Different tokens
By convention, hyperdata nodes could be started by using an @
notation. Data nodes that are children of hyperdata nodes are attributes of the hyperdata nodes and @
nodes start new hyperdata nodes recursively.
For example:
{
"type": "Issues",
"description": "The list of issues we are tracking",
"@form": {
"name": "create",
"action": "/create.php",
"method": "POST",
"@label": "Create new issues",
"@input": [{
"name": "title",
"required": "true",
"@label": "The name of the issue"
}, {
"name": "description",
"@label": "The description of the issue"
}]
}
}
You can assign properties at the key name of the node too between [...]
, which gives you the ability to repeat elements with the same key/name. For example:
{
"type": "Issues",
"description": "The list of issues we are tracking",
"@form[name:'create', action:'/create.php', method:'POST']": {
"@label": "Create new issues",
"@label[for:'title']": "The name of the issue",
"@input[name:'title']": {
"required": "true"
},
"@label[for:'description']": "The description of the issue"
"@input[name:'description']": {}
}
}
JSON-ISH
If backwards compatibility with JSON parsers wasn’t an issue, we could extend the grammar to enable the intermingling of data and hyperdata. In the JSON-ish formulation, we would have something along the lines of:
{
a: 1,
b: 2,
form {
...
input {
...
}
input {
...
}
}
}
Breaking JSON compatibility requires a major advantage in return, which doesn’t seem to stand on its own weight.