What Is Collection+JSON?

The Collection+JSON (Cj) specifies a hypermedia type for collections of items. It not only defines how single items or lists are represented in a document, it also comes with application semantics how to expose a standard CRUD behavior for items. In addition to that, embedded HATEOAS hyperlinks are one of the building blocks for this format.

And, hey, all that is done in lovely JSON!!!

Why not assume our music API would suddenly support the Cj media format? Here’s what I’d GET for http://songs/scarifice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
 "collection" :
 {
  "version" : "1.0",
  "href" : "http://songs/",
  "items" :
  [
   {
    "href" : "http://songs/scarifice",
    "data" : [
     {"name": "title", "value": "Scarifice"},
     {"name": "band",  "value": "Racer X"}
    ],
    "links" : [
     {"rel": "band", "href": "http://bands/racer-x"}
    ]
   }
  ]
 }
}

The Cj format surprisingly always “speaks in” collections – even the single song is represented in a 1-item collection. The format provides a version, the hyperlink href to the collection itself, and the actual items we’re interested in (line 6).

Items in turn can have a href pointing to itself, properties of the represented object are found in the data attribute (line 10), and items may also contain additional links. Here, the song references the API endpoint of the band owning that very song (line 15).

Note that “real” collections have the same format, e.g. when grabbing http://songs the items array is be filled with millions of damn good song items.

Application Semantics Of Cj.

Apart from the standardization of representing lists this is nothing special so far. The cool thing about Cj is that it comes with specifications about the CRUD behavior of collections. Here’s an example.

If we were to create another song without knowing the API semantics we were helpless! Not with Cj! Here, a collection usually comes with a template telling us how to create or update particular items.

The template is embedded in the collection itself, in the document we were just talking about.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
 "collection" :
 {
  "version" : "1.0",
  "href" : "http://songs/",
  "items" : 
 ...
  "template" : {
   "data" : [
     {"name": "title", "value": ""},
     {"name": "band",  "value": ""}
   ]
  }
 }
}

Normally, you’d fill out a form and submit it to create something. That’s exactly what the template does – it acts like a form which is to be POSTed to the collection’s href URL. Remember, that was http://songs/ (line 5)?

The corresponding create request would be something like the following.

1
2
3
4
5
6
7
8
9
10
POST http://songs/
------------------
{
 "template" : {
  "data" : [
   {"name": "title", "value": "Parasite"},
   {"name": "band",  "value": "Frenzal Rhomb"}
  ]
 }
}

Cj comes with more, it contains a query specification for retrieving other collections, an error structure for exposing internal errors and more. I suggest you check out the examples the creator Mike Amundsen provides on his site.

Implementing Cj Services.

Too bad media formats only describe documents and how they might be used. It is still up to you to actually implement those. To make your life simpler the roar gem now comes with a Collection+JSON representer

By the time of writing this roar gives you a representer and a bit of added DSL sugar to render and parse collection representations. I won’t go into detail in this posting as the API is not 100% stable and we’re waiting for your comments. Here’s how a full-blown representer for the songs domain would look like.

module SongCollectionRepresenter
  include Roar::Representer::JSON::CollectionJSON
  version "1.0"
  href { "//songs/" }
 
  link(:feed) { "//songs/feed" }
 
  items(:class => Song) do
    href { "//songs/scarifice" }
 
    property :title, :prompt => "Song title"
    property :length, :prompt => "Song length"
 
    link(:download) { "//songs/scarifice.mp3" }
    link(:stats) { "//songs/scarifice/stats" }
  end
 
  template do
    property :title, :prompt => "Song title"
    property :length, :prompt => "Song length"
  end
 
  queries do
    link :search do
      {:href => "//search", :data => [
        {:name => "q", :value => ""}]}
    end
  end
end

The DSL should be pretty self-explaining. However, suggestions welcome! I tried to use as much roar/representable language as possible. Those guys already familiar with roar will recognize the property and link semantics for sure. Semantics. I love this word. Although I don’t understand its semantic.

Having defined the Cj representer you may now render and parse documents.

[Song.find(1)].
  extend(SongCollectionRepresenter).
  to_json

Note that the collection representer works with enumerable objects, like arrays, only.

Parsing an incoming document, e.g. in a client is just as simple as the rendering.

[].
  extend(SongCollectionRepresenter).
  from_json("{collection: ...")

Please, play around with it! Since Rubygems is only partially working you should go with the github master of roar. Have fun!

7 Responses to “Collection+JSON Support In Roar!”


  1. Karlin

    I’d love to use roar, but I’m concerned with the use of OpenStruct (in Hyperlink class) and .extend (for mixing in Representers), meaning Ruby will constantly invalidate its method cache…(http://charlie.bz/blog/things-that-clear-rubys-method-cache)

    Have you heard of any performance problems when people have used roar or representer ‘in production’?


  2. nick

    Karlin: The Hyperlink class using OpenStruct shouldn’t be a performance killer but we can investigate on that. And, good news for you: the upcoming version of roar/representable supports a decorator strategy avoiding extend :-)


  3. nick

  4. Karlin

    Awesome, thanks for adding Decorator. And thanks for working on Roar and Representable in general, they’re great!

    I tried out the new Roar::Decorator yesteday but ran into trouble using Roar::Representer::JSON::HAL (I decided to use HAL instead of Collection+JSON.) and the ‘link’ helper:

    link :self { “/widgets/#{represented.id}” }

    It was still trying to call ‘links’ on the represented object (let’s call it ‘Widget’) instead of on my Roar::Decorator subclass (‘WidgetsRepresenter’):

    undefined method `links’ for #

    It looks like some modules (like LinkCollectionRepresenter) in ‘lib/roar/representer/json/hal.rb’ are still assuming they should use ‘extend’? Any ideas?


  5. Karlin

    Regarding OpenStructs in Hyperlink, maybe this could help: https://github.com/charliesome/fast_open_struct


  6. nick

    @Karlin: Thanks for reporting that bug, I am already working on it!


  7. nick

Leave a Reply