JSON schemas for various Entity types and their natural behaviours
Entities may represent:
- People: a user, their profile, their resume/CV
- People: a list of contacts
- Places: a structure in 2D or in 3D
- Places: a place on a real or virtual map
- Events: a feed, or an article within a feed
- Events: a Calendar entry
- Things: a whiteboard, a photo gallery
- Types - 3D Entities; OpenID/vCard - static stuff
Laws - 3D bit has much to-fromming to do, including mv/cp/ln JXEE stuff
- Now, wrapper and overlay are 'rules' (i.e., not instances) and are same thing
- New 'heldby': list of holders you care about (needn't know/care about all ways in - see WWW)
User: add location/looking at and avatar links
NT talks wraps, text, lists, feeds, and is the actual domain format standards and behavioural rules
Pages are rendered by recursively walking a tree of JSON structures and building up a page from XHTML snippets via a templating mechanism.
There are two most basic JSON schemas used in creating pages: Viewlist and MML. Page templates are output in Viewlist format, which are nested lists, horizontal and vertical. Articles and text are in 'MML' - a simple Wiki-markup-like syntax.
At the top level JSON hash/object, 'meta' / 'wrapper' / 'overlays' / 'interest' / 'message' can appear before a 'classed' thing (viewlist, mml, etc). More detail on metadata, wrappers, overlays and interest appears below, but briefly:
{
"meta": { "title": "Introduction" },
"wrapper": "-517e-4ead-",
"overlays": "-c800def6-1518-11dd-a131-00e000c4ccc4-",
"interest": "user:vcard:role:Editor",
"message": "Your preferences have been changed",
"viewlist": ..
}
Metadata can be merged in to the page metadata from any level in the JSON tree. Lower metadata overrides higher, except for title, where it is prepended with a '|' vertical bar. Only HTML metadata goes into HTML meta fields. Other metadata may have other effects.
{
"meta": { "name": "Site 2.0",
"title": "Super Site",
"tags": [ "site", "Super Site", "Analysis" ],
"description": "The premier site for intelligent talk",
"atom": "http://site.com/atom/",
"rss": "http://site.com/rss2/"
},
:
}
Minimal is just a title:
{
"meta": { "title": "site.com" },
:
}
Here's the full list:
AWC - Atom feed or entry; Website; Component
|||
* "website": "http://host.org/",
* "weblink": "http://host.org/stuff/234-234",
* "atom": "http://host.org/atom/",
* "rss": "http://host.org/rss2/",
* "search": "http://host.org/opensearch.xml",
* "logo": "http://host.org/favicon.gif",
*** "icon": "http://host.org/favicon.ico",
* "license": "http://creativecommons.org/xyz2.0"
*** "title": "Wild Bunnies on the rampage in Dorset",
* "subtitle": "Up to 20 wild bunnies were caught on CCTV",
* "slug": "microsoft-option-yahoo",
*** "description": "The best bunny watching website in the world",
* "summary": "Wild bunnies were found rampaging through the sleepy Dorset town of Bunnikill",
* "name": "Bunnies.org 2.0",
* "published": "2006-12-14T14:15:00Z",
* "updated": "2006-12-14T14:15:00Z",
* "version": 542,
** "author": { "user": { "vcard": { "fullName": "Full Name" } } },
** "author": "-link-to-vcard-",
* "license": "-link-to-license-"
** "contact": "-link-to-vcard-",
* "generator": "host.org feeder",
* "rights": "All content including photos and images by Host.org. Copyright (c) Host.org",
*** "tags": [ "architecture", "declarative" ]
*** "language": "en-UK"
If a JSON resource calls up to a wrapper, it must have a 'hole' to put it into:
111-111:
{
"wrapper": "-111-222-"
"mml": [ "wrapped text" ]
}
111-222:
{
"meta": { "title": "site.com" },
"viewlist": { "-order-": ["wraps"], "wraps": "-x-" }
}
Here, 111-222 wraps 111-111, giving the page a title. The URL is to 111-111.
Inside lists, use =[ .. , "wraps: -x-", .. ]=. Note the internal id ='-x-'= is replaced by the UID of the wrapped JSON, which in turn is replaced by the full JSON being wrapped.
See Entity Rules for the overlay syntax.
The 'interest' field is a pattern or list of patterns showing what range of events this resource is going to respond to in the form of POST (or its own GET). It's similar to an HTML form in some ways.
A JSON resource may have a representation in a user interface. A prime use of the interest field is in enabling interfaces to be decorated accordingly. This boils down to write permissions, usually.
Here is an example interest pattern:
"interest": [ "user:vcard:fullName:Brian", { "user": { "vcard": { "role": "*" } } } ]
The pattern can either be like the linear form shown first, or like
the full skeleton object shown second. They follow the above
unification syntax.
Examples of the use of this field appear later in this document.
Minimal (vertical list of other three other JSONs, each element of which with class="vertlist"):
{ "meta": { "title": "site.com" },
"viewlist": [ "itemtag:.vertlist", "-123-abc-", "wraps: -x-", "-def-456-" ]
}
Viewlists can go straight into nested lists, including having another 'viewlist' layer before the next list down, usually included from another JSON.
Lists are either ordered hashes or JSON lists. Any list can have a direction: horizontal or vertical, a liststate: open or closed, an itemstate list, a set of options (collapsible, tabbed, smooth, reorderable, etc) and a class for the whole list or for each item (listtag and itemtag).
{ "direction":"vertical", "liststate":"closed", "options":"smooth", "listtag":".skyline.skylist", .. }
In JSON lists, these must appear at the head of the list in this form:
[ "direction:horizontal", "liststate:open", "options:smooth", "listtag:.skyline.skylist", .. ]Defaults: =direction: vertical, liststate: open=
Of course, =collapsible= and =liststate: open/closed= only applies to CSI, or SSI with some kind of URL structure.
If you have an ordered hash list with user-defined tags like '.this', they get converted to classes like class="this" in the final XHTML. Also, '.this.that' becomes class="this that". If you say "#this", it's converted to id="this". Of course, '.this#that' becomes id="that" class="this".
Simple template layout:
{
"meta": { "title": "Simple Site" }
"viewlist": {
"-order-": [ "#header", "#middle", "#footer" ],
"direction": "vertical",
"#header": "-4ead-ba7-",
"#middle": {
"-order-": [ "#left", "wraps", "#right" ],
"direction": "horizontal",
"#left": "-1ef7-ba7-",
"wraps": "-x-",
"#right": "-717e-ba7-"
},
"#footer": "-f007-ba7-"
}
}
Inheriting templates: master template has basic structure with holes for content, comment and rail:
{
"meta": { "title": "Super Site" }
"viewlist": {
"-order-": [ "#header", "#middle", "#footer" ],
"#header": {
"-order-": [ "#advert", "#header", "#navigation", "#links" ],
"#advert": "-8663a986-153d-11dd-a131-00e000c4ccc4-",
"#header": "-7414ea72-1530-11dd-a131-00e000c4ccc4-",
"#navigation": "-fce4c464-1533-11dd-a131-00e000c4ccc4-",
"#links": "-d3a5d5d0-153c-11dd-a131-00e000c4ccc4-"
},
"#middle": {
"-order-": [ "#content", "#right" ],
"direction": "horizontal",
"#content": { },
"#right": {
"-order-": [ "#comment", "#rail" ],
"direction": "vertical",
"#comment": { },
"#rail": { }
}
},
"#footer": "-f1917d1a-153c-11dd-a131-00e000c4ccc4-"
}
}
Inheriting templates: second-level template adds a rail with an advert:
{
"overlays": "-312029b2-1516-11dd-a131-00e000c4ccc4-",
"*":"*",
"viewlist": {
"-order-": [ "#middle" ],
"#middle": {
"-order-": [ "#right" ],
"#right": {
"-order-": [ "#rail" ],
"#rail": {
"-order-": [ "#advert" ],
"#advert": "-4c1e53bc-15cd-11dd-935c-00e000c4ccc4-"
}
}
}
}
}
Inheriting templates: bottom-level template has hole for wrapped content:
{
"overlays": "-c800def6-1518-11dd-a131-00e000c4ccc4-",
"*":"*",
"viewlist": {
"-order-": [ "#middle" ],
"#middle": {
"-order-": [ "#content" ],
"#content": {
"-order-": [ "wraps" ],
"wraps": "-x-"
}
}
}
}
Moving and animating any content in Javascript requires representing
it at the bottom, say, above the footer in the JSON form, with
enough class triggers to allow it to be repositioned, floated, etc.
Skyline:
555-444-333:
{
"meta": { "version": 12 },
"interest": [ "user:vcard:role:Editor" ],
"viewlist": {
"-order-": [ ".li1", ".li2", ".li3" ],
"direction": "horizontal",
"options": "reorderable cuttable pastable",
"listtag": ".skyline",
"itemtag": ".sky-item",
".li1": {
"-order-": ["skyimage",".resize"],
"direction": "horizontal",
"skyimage": "[Jack Maguire][http://...jpg][http://../u/11-11.u]",
".resize": "##[[![Jack Maguire]! office life][http://../u/11-11.u]]##"
},
".li2": {
"-order-": ["skyimage",".resize"],
"direction": "horizontal",
"skyimage": "[Joe Johnson][http://...jpg][http://../u/22-22.u]",
".resize": "##[[![Joe Johnson]! things][http://../u/22-22.u]]##"
},
".li3": {
"-order-": [ ".li1", ".li2", ".li3" ],
"options": "reorderable cuttable pastable",
"listtag": "ul.skylist",
"itemtag": "li.sky-item",
".li1": "###[[![Bush gets it wrong]! Persistence is key][http://../u/33-33.u]]###",
".li2": "###[[![Elections crisis]! A fine mess][http://../u/44-44.u]]###",
".li3": "###[[![Alliance and Leicester]! The takeover chances][http://../u/55-55.u]]###"
}
}
}
The JSON-to-HTML mapping rules are:
Applying these rules gives:
<div class="jsoninclude u-555-444-333 meta-version-12 interest-user-vcard-role-Editor viewlist-h reorderable cuttable pastable skyline">
<div class="sky-item li1 viewlist-h">
<a href="http://../u/11-11.u"><img src="http://..jpg" alt="Jack Maguire" /></a>
<div class="resize"><h2><a href="http://../u/11-11.u"><strong>Jack Maguire</strong> office life</a></h2></div>
</div>
<div class="sky-item li2 viewlist-h">
<a href="http://../u/22-22.u"><img src="http://..jpg" alt="Joe Johnson" /></a>
<div class="resize"><h2><a href="http://../u/22-22.u"><strong>Joe Johnson</strong> things</a></h2></div>
</div>
<ul class="sky-item li3 viewlist-v reorderable cuttable pastable skylist">
<li class="sky-item li1">
<h3><a href="http://../u/33-33.u"><strong>Bush gets it wrong</strong> Persistence is key</a></h3>
</li>
<li class="sky-item li2">
<h3><a href="http://../u/44-44.u"><strong>Elections crisis</strong> A fine mess</a></h3>
</li>
<li class="sky-item li3">
<h3><a href="http://../u/55-55.u"><strong>Alliance and Leicester</strong> The takeover chances</a></h3>
</li>
</ul>
</div>
The Javascript checks the user is an editor, sees 'reorderable',
then looks for li1, li2, etc.
Notice how not having a class or tag means not having a wrapping element (default div). Hence a viewlist that is a JSON list rather than the ordered hashes above will only wrap its content in a div or other tag if itemtag is set.
See the examples page page for edits over these structures.
Other examples - header, footer, rail component:
{
"viewlist": {
"-order-": ["#breadcrumbandedition","#logoandsearch","#skyline"],
"direction": "vertical",
"#breadcrumbandedition": {
"direction": "horizontal",
"-order-": ["#breadcrumb","#edition"],
"#breadcrumb": "![site.com]!/frontpage",
"#edition": "[UK][/index.html#vpsite=-123-451-] | [Europe][/index.html#vpsite=-123-452-]"
},
"#logoandsearch": {
"direction": "horizontal",
"-order-": ["#logo","#search"],
"#logo": "-site/logobig.jpg-",
"#search": "-789-101-"
},
"#skyline": "-5ky-l1n3-"
}
}
{
"viewlist": {
"-order-": [ "#copyright", "#footlinks" ],
"direction": "vertical",
"#copyright": "![© The Super Site Ltd 2008]! 'Super Site' is a trademark.",
"#footlinks": "-f00t-link5-"
}
}
{ "meta": { "title": " " },
"viewlist": {
"-order-": [".content", ".advert", ".atomfeed"],
"options": "collapsible microtitle reorderable",
"liststate": "open",
".content": "This is some content",
".advert": "-adv-3rt-",
".atomfeed": "-http://duncan-cragg.org/blog/atom.u-"
}
}
Tabbed list:
{
"viewlist": {
"-order-": [ ".searchtab", ".browsetab" ],
"direction": "horizontal",
"options": "tabbed microtitle",
"itemstate": "closed, open",
".searchtab": {
"meta": { "title": "Search", },
"search": ..
},
".browsetab": {
"meta": { "title": "Browse", },
"search": ..
}
}
}
The 'microtitle' option means use the meta:title off each element of
the list as the 'closed' form - i.e., in this case, the name of
the tab. Here, 'itemstate' has the first one closed and 'remaining
ones' open (the last itemstate - or itemtag come to that - applies
to all remaining) - but there is only one remaining, here.
A table is like a two-level viewlist that goes vertical then horizontal or horizontal then vertical, with row and/or column headers. It's flagged in the same way as ul/li - using table/tr if vertical-first:
{
"viewlist": [
"listtag:table.searchresults",
"itemtag:th.searchresultsheaders, tr.searchresultsentry",
[ "Title", "Date", "Author", "Folder" ],
[ "[Mugabe steps down][http://..]", "3-Jul-2008", "Jim McFarley", "Front Page UK" ],
[ "[Pearson shares up][http://..]", "2-Jul-2008", "Kath Smith", "Companies" ]
]
}
Here, the outer list is a vertical list of rows, the inner lists are
horizontal lists of columns ("direction:horizontal" is optional or
redundant when it's a table). The first row is a header row with a
class of 'searchresultsheaders' from the first itemtag. The second
row and all subsequent rows have the class 'searchresultsentry'. The
table's class is 'searchresults'.
Allows headers, paragraphs, bold, italic (sorry: strong, emphasis), links, images, lists, tables to be expressed.
{
"meta": {
"title": "Microsoft easy target, says Yahoo",
"author": "-link-to-user-vcard-",
"published": "2006-12-14T14:15:00Z",
"updated": "2006-12-14T14:15:00Z",
"weblink": "http://..//..",
"version": 8429,
"tags": [ "microsoft", "yahoo" ]
},
"mml": [
"##[Second-level header]##",
"New paragraph: text /[italics]/ and ![bold]! and |[monospace]|.",
"New paragraph: a [link][http://host.org/path/]."
"New paragraph: An image [this is the alt text][http://host.org/path/image.jpg][http://host.org/path/]",
"..images are always clickable unless you leave the link empty"
]
}
{
"meta": { "title": "Introduction" },
"wrapper": "-4ead-f007-",
"mml": [
"My name is [Duncan Cragg][http://duncan-cragg.org/blog/].",
"The following 'paragraph' is a ul/li:",
[ "item 1", "item 2", "item 3" ],
"The following 'paragraph' is a table:",
[ [ "item 1", "item 2", "item 3" ], [ "item 1", "item 2", "item 3" ] ],
"The following 'paragraph' is an embedded viewlist:",
{ "-order-": [ "sub" ],
"options": "collapsible microtitle",
"liststate": "closed",
"sub": "-123-789-"
},
"The following 'paragraph' is an embedded sub-object:",
"-123-abc-"
]
}
{
"mml": [
"A ul/li nav:",
[
"[Front page][/front.html]",
"[This][/this.html]",
"[That][/that.html]",
"[The Other][/the-other.html]"
]
]
}
A feed is just a list of things (list or ordered hash), with certain metadata usually present, that forms a sliding, time ordered window. News and blogs will be a time-ordered list of MML entries:
{
"meta": {
"title": "Need to know",
"rights": "Super Site",
"icon": "http://site.com/favicon.ico",
"atom": "http://site.com/atom/",
"updated": "2008-02-16T23:44:00Z",
"version": 29,
"generator": "super cms",
},
"feed": [
{ "meta": {
"title": "Foo Fighters Found in Frankfurt",
"author": "-link-to-hcard-",
"summary": "Foo Fighters were found in Frankfurt",
"updated": "2008-02-16T23:44:00Z",
"weblink": ".."
},
"-etc-": "-../atom.u-"
},
{ "meta": {
:
},
"-etc-": "-../atom.u-"
}
]
}
Here's a fuller example, with three different ways to include entries:
{
"meta": {
"title": "Title",
"subtitle": "Subtitle",
"author": { "user": { "vcard": { "fullName": "Full Name" } } },
"logo": "http://host.org/favicon.gif",
"icon": "http://host.org/favicon.ico",
"rights": "All content including photos and images by Super Inc. Copyright (c) Super Inc",
"generator": "site.com feeder",
"website": "http://site.com/",
"atom": "http://site.com/atom/",
"version": 542,
"updated": "2008-02-16T23:44:00Z"
},
"feed": [
"-http://host.org/stuff/atom.u-",
{ "meta": {
"title": "entry title 2",
"author": "-link-to-hcard-",
"summary": "summary 2",
"updated": "2008-02-16T23:44:00Z",
"weblink": "http://site.com/stuff2/"
},
"-etc-": "-http://site.com/stuff2/atom.u-"
},
{ "meta": {
"title": "entry title 3",
"author": "-link-to-hcard-",
"summary": "summary 3",
"updated": "2007-10-05T11:22:00Z",
"weblink": "http://site.com/stuff3/",
"tags": [ "stuff", "things" ],
"version": 3
},
"mml": [ "This is an embedded entry" ]
},
"-etc-": "-link-to-next-chunk-in-feed-"
]
}
http://site.com/stuff2/atom.u:
{ "meta": {
"title": "entry title 2",
"author": "-link-to-hcard-",
"summary": "summary 2",
"updated": "2008-02-16T23:44:00Z",
"weblink": "http://site.com/stuff2/",
"tags": [ "stuff", "things" ],
"version": 2
},
"mml": [ "This is a linked entry" ]
}
The =-etc-= at the end of the feed makes it an endless list - the
next one is a bucket filling up with the overflow from the top,
say 10, items in the head feed. Once this bucket itself reaches 10,
it becomes the second bucket and a new overflow bucket is inserted.
Thus any bucket, once filled up, is constant for ever more, and the
head is always on the same URL.
Atom's 'previous' and 'last' links aren't supported this way. Usually, a list is ordered in a most-useful order (time, relevance, etc), and the least interesting is the set of results or entries at the bottom of the list. Similarly, traversal is from most-useful down to less, so 'previous' is pretty unlikely, too.
Notice that we've already got the xFolk Microformat in the above feed summary:
<div class="xfolkentry">
<div>
<a class="taggedlink" href="http://www.sifry.com/alerts/archives/000306.html">
Sifry's Alerts: Technorati launches Related Tags
</a>
</div>
<div class="description">
Ever wanted to see what posts are related to other posts..
</div>
<div class="meta">
to
<a rel="tag" href="/tag/folksonomy">folksonomy</a>
<a rel="tag" href="/tag/technorati">technorati</a>
... on 2005-04-09
</div>
</div>
becomes:
{ "meta": {
"title": "Sifry's Alerts: Technorati launches Related Tags",
"summary": "Ever wanted to see what posts are related to other posts..",
"updated": "2005-04-09",
"weblink": "http://www.sifry.com/alerts/archives/000306.html",
"tags": { "folksonomy": "/tag/folksonomy", "technorati": "/tag/technorati" }
},
"-etc-": "-http://www.sifry.com/alerts/archives/000306.u-",
},
.. where the =-etc-= bit is only in the case that Mr Sifry offers
this Alert in conformant JSON.. Otherwise, this is pure metadata
because there's no JSON content to which it refers. The weblink
(Atom's link rel=alternate, type=text/html) is the subject of this
metadata.
This format re-appears in search..
Search has a form for setting various parameters and a results list or table.
The OpenSearch specification standardises search requests and results. We have a JSON serialisation or syntax variant of their semantics.
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Web Search</ShortName>
<Description>Use Example.com to search the Web.</Description>
<Tags>example web</Tags>
<Contact>admin@example.com</Contact>
<Url type="application/atom+xml"
template="http://example.com/?q={searchTerms}&pw={startPage?}&format=atom"/>
<Url type="application/rss+xml"
template="http://example.com/?q={searchTerms}&pw={startPage?}&format=rss"/>
<Url type="text/html"
template="http://example.com/?q={searchTerms}&pw={startPage?}"/>
<LongName>Example.com Web Search</LongName>
<Image height="64" width="64" type="image/png">http://example.com/websearch.png</Image>
<Image height="16" width="16" type="image/vnd.microsoft.icon">http://example.com/websearch.ico</Image>
<Query role="example" searchTerms="cat" />
<Developer>Example.com Development Team</Developer>
<Attribution>
Search data Copyright 2005, Example.com, Inc., All Rights Reserved
</Attribution>
<SyndicationRight>open</SyndicationRight>
<AdultContent>false</AdultContent>
<Language>en-us</Language>
<OutputEncoding>UTF-8</OutputEncoding>
<InputEncoding>UTF-8</InputEncoding>
</OpenSearchDescription>
becomes:
{ "meta": { "title": "Web Search",
"subtitle": "Example.com Web Search",
"description": "Use Example.com to search the Web.",
"tags": [ "example", "web" ],
"author": { "user": { "vcard": { "fullName": "Example.com, Inc." } } },
"contact": { "user": { "vcard": { "email": "admin@example.com" } } },
"website": "http://example.com/",
"icon": [ "http://example.com/websearch.png", "http://example.com/websearch.ico" ],
"rights": "Search data Copyright 2005, Example.com, Inc., All Rights Reserved",
"language": "en-UK"
},
"search": { "parameters": { "searchTerms": { "value": "*", "label": "Search Text", "example": "cat" },
"startPage": { "value": "*", "label": "Start Page" }
}
}
}
The searchTerms parameter allows search for all keywords in any
order inside all text, including title, summary and tags. Without
a label, it may default to "Search". Other parameters defined by
OpenSearch are =count, startIndex, language=. Encoding is always
UTF-8, as defined by JSON.
Using vCard elements for parameters:
"search": { "parameters": { "vcard:address:postalCode": { "value":"*", "label": "Enter Post Code" } } }
"search": { "parameters": { "vcard:address:postalCode": { "value":"*" } } }
The second one may have a default label of 'Postal Code'.
Using meta elements for parameters:
"search": { "parameters": { "meta:title": { "value":"*", "label": "Title to find" } } }
Pattern matching in an MML body:
"search": { "parameters": { "mml": { "value": "*", "label": "Enter regular expression", "example": "/.*a.*b.*/" } } }
"search": { "parameters": { "mml": { "value": "/one/two/" } } }
First one is for the rare user who can type regular expressions
without going cross-eyed. The second one matches ".. two .. one ..
", etc. (concatenating regular expression matches /like/this/ means
'AND' in any order). It has no label, so the interface will default
to some vanilla label like 'Search body text'.
Override the default implied by the parameter name:
"search": { "parameters": { "meta:version": { "value":"*", "label": "Version (2-20)", "range": [ 2, "...", 20 ] } } }
Note that a search form allowing entry for a parameter must allow it
to be underspecified: the title could be just a few possible
keywords, a date could be entered as a date range, etc. Date
ranges look like this:
[ "2008-12-10T12:00:00Z", "...", "2008-12-12T23:30:00Z" ]..or this:
[ "2008-12-10", "...", "2008-12-12" ]
The OpenSearch query:
<Query role="request" searchTerms="New York" startPage="1" />becomes:
{ "user": { "applying": {
{ "search": { "parameters": { "searchTerms": { "value": "New York" }, "startPage": { "value": 1 } } } }
} } }
which uses the 'user:applying' pattern described later, allowing
personalisation of the results.
This gives the OpenSearch response:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
<title>Example.com Search: New York</title>
<link href="http://example.com/New+York"/>
<updated>2003-12-13T18:30:02Z</updated>
<author>
<name>Example.com, Inc.</name>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<opensearch:totalResults>4230000</opensearch:totalResults>
<opensearch:startIndex>21</opensearch:startIndex>
<opensearch:itemsPerPage>10</opensearch:itemsPerPage>
<opensearch:Query role="request" searchTerms="New York" startPage="1" />
<link rel="alternate" href="http://example.com/New+York?pw=3" type="text/html"/>
<link rel="self" href="http://example.com/New+York?pw=3&format=atom" type="application/atom+xml"/>
<link rel="first" href="http://example.com/New+York?pw=1&format=atom" type="application/atom+xml"/>
<link rel="previous" href="http://example.com/New+York?pw=2&format=atom" type="application/atom+xml"/>
<link rel="next" href="http://example.com/New+York?pw=4&format=atom" type="application/atom+xml"/>
<link rel="last" href="http://example.com/New+York?pw=42299&format=atom" type="application/atom+xml"/>
<link rel="search" type="application/opensearchdescription+xml" href="http://example.com/opensearchdescription.xml"/>
<entry>
<title>New York</title>
<link href="http://www.columbia.edu/cu/lweb/eguids/amerihist/nyc.html"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated>
<content type="text">
... Harlem.NYC - A virtual tour and information on
businesses ... with historic photos of Columbia's own New York
neighborhood ... Internet Resources for the City's History. ...
</content>
</entry>
</feed>
which becomes:
{ "meta": { "title": "Web Search",
"subtitle": "Example.com Web Search: New York",
"description": "Use Example.com to search the Web.",
"tags": [ "example", "web" ],
"author": { "user": { "vcard": { "fullName": "Example.com, Inc." } } },
"contact": { "user": { "vcard": { "email": "admin@example.com" } } },
"website": "http://example.com/",
"icon": [ "http://example.com/websearch.png", "http://example.com/websearch.ico" ],
"rights": "Search data Copyright 2005, Example.com, Inc., All Rights Reserved",
"language": "en-UK",
"updated": "2003-12-13T18:30:02Z",
"weblink": "http://example.com/New+York?pw=1",
"search": "http://example.com/opensearchdescription.xml"
},
"search": { "parameters": { "searchTerms": { "value": "New York", "label": "Search Text" },
"startPage": { "value": 1, "label": "Start Page" }
},
"info": { "totalResults": 4230000 },
"response": [
{ "meta": {
"title": "New York",
"summary": "... Harlem.NYC - A virtual tour and information on businesses ... ",
"updated": "2003-12-13T18:30:02Z",
"weblink": "http://www.columbia.edu/cu/lweb/eguids/amerihist/nyc.html"
}
},
:,
"-etc-": "-link-to-next-page-of-results-"
]
}
}
We don't use 'feed', because the order is best-match, not time. In
fact, the only real difference is not using the word 'feed', but
'response'. Also, this is essentially an extended or grounded
version of the original search spec, with defined search parameter
values and extra 'info' and 'response' elements. The 'info' may
include additional OpenSearch Query roles:
"info": { "totalResults": 4230000,
"correction": { "searchTerms": { "value": "New Yoik" }, "title": "Didjamean?", "totalResults": 62340000 },
"related": { "searchTerms": { "value": "New Jersey" }, "title": "Whatabout?", "totalResults": 8320130 },
"subset": { "searchTerms": { "value": "New Yorkers"}, "title": "Narrowdown", "totalResults": 2340000 }
}
The JSON form of the OpenSearch response is slightly different from
the XML form in that the latter was for page three, and the startIndex
was 21. We don't have pages like that, just an =-etc-= link, so this
shows page one only. We don't need startIndex or itemsPerPage with
our approach, as it's just a list, and you can use list indexing and
list length to discover those things.
It does imply the server saving at least some state - at least the query and page number - to dereference the next page, if not the full page of results ready to read. A search engine running off a query string could generate the next page on demand with no saved state at all.
Sort order - the server can send back the parameter on which the results were sorted, and thus allow sorting on a different parameter - usually by hitting the results table column header:
"search": { "parameters": { "searchTerms": { "value":"*" }, "sortBy": { "value": "meta:updated" } } }
Here, the =search:parameters:sortBy= in the response tells us that the
results were sorted on the updated timestamp. Re-sorting requires a
repeat query, with the =sortBy= parameter added.
This one is totally up for discussion.
{
"advert": {
"baseurl": "http://ads.site.com/html.ng/"
"params": { "usequery": false, "site": "sitecom", "pos": "mpu_pagestest", "artid": "test12" }
}
}
Gives =http://ads.site.com/html.ng/site=sitecom&pos=mpu_pagestest&artid=test12=
I don't know if we're supposed to drop the query before the params, but 'usequery' can be set to switch it on and off.
A user in the browser is a first-class object, with its own JSON representation of its current state. This JSON state is 'animated' by the user's behaviour at the browser.
A user contains a vCard (see next section), and can have OpenID and chat elements, as well as links to profiles and preferences.
Up to now we've used a simple vCard in the user object:
{ "user": { "vcard": { "fullName": "Brian", "role": "Business Analyst" } } }
Here is a fuller one - this is a JSON serialisation of the vCard spec, with expanded forms of the over-terse field names (e.g. 'N' now becomes 'name'):
{ "user":
{ "vcard": {
"fullName": "Mr Brian Rock",
"role": "Business Analyst"
"name": {
"given": "Brian",
"middle": [ "Andy", "Charles" ],
"family": "Rock"
},
"nickname": "The Masher",
"address": {
"street": [ "My Mews", "My Street" ],
"locality": "My City",
"region": "My County",
"postalCode": "MY90 CDE",
"countryName": "UK"
},
"email": [ "brian.rock@site.com", "bri2001@hotmail.com" ],
"organisation": {
"name": "Super Site",
"unit": "site.com"
},
"title": "Business Analyst",
"birthday": "1995-02-12",
"tel": { "work": [ "020 7231 1234", "020 7231 3212" ], "home": "020 8323 2343" },
"url": [
"http://brian.org/",
"http://facebook.com/brio"
],
"description": "I love cats"
} }
}
Some fields may be singular or plural (e.g. address may be a list of
hashtables); all tags are camelCase.
A user (and, indeed any other resource) can 'apply' to have another resource change, or it can request to 'apply' a template or overlay to it. REST isn't about databases - any resource can refuse to be updated, or can make arbitrary modifications to the edit suggestion before applying it.
Here's what 'applying' an overlay by a user looks like:
{ "user": { "vcard": { "fullName": "Brian", "role": "Business Analyst" },
"applying": { "feed": [
{ "viewlist": {
"type": "skyline",
"uuid": "123-321"
} },
"*:*"
]
}
} }
Everything after "applying" is a desired state of a target resource,
declared through use of the overlay syntax.
There is a complete description of various interactions between systems in the examples page.
There are a number of cases where part of the rendering of the JSON data can result in generating a Microformat:
[2278]