Generalized Linking

by Subbu Allamaraju on October 10, 2008

Jonas Galvez recently asked in the rest-discuss list whether to use Atom style link element, or roll his own.

There were a few more discussions recently on this topic in the context of JSON. One was by Joe Gregorio on RESTful JSON where he talked about using a href property for linking. The second was a proposal JSON Resources: A proposal for a RESTful JSON protocol by Kris Zyp in the restful-json group which took a very complicated approach by suggesting use of properties like $ref for linking.

(The following notes has been lying around for sometime on my desk, and this is an attempt to clear it and get some feedback.)

For Atom Representations

Use the link element defined by Atom, such as the following.

<atom:link rel="self" href="http://example.org/movie/1234"/>
<atom:link rel="edit" href="http://example.org/movie/1234?view=full"/>

Although Atom defines the link element as a child of atom:feed and atom:entry elements, I would take a step further and encourage using this element even within atom:content, particularly for linking from/to inner/sub resources. Here is an example that I frequently use.

<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
            xmlns:xml="http://www.w3.org/XML/1998/namespace">
    <atom:author>
        <atom:name>Blah blah </atom:name>
    </atom:author>
    <atom:title>Fiddler on the Roof</atom:title>
    <atom:link rel="self"
    href="http://example.org/movie/1234"/>
    <atom:id>urn:movies:movie:1234</atom:id>
    <atom:published>2007-09-06T00:00:00Z</atom:published>
    <atom:updated>2007-09-06T00:00:00Z</atom:updated>
    <atom:content xml:lang="en-US" atom:type="application/vnd.exaple.movies+xml">
        <movie xmlns:atom="http://www.w3.org/2005/Atom">
            <title>Fiddler on the Roof</title>
            <year>1971</year>
            <duration>181</duration>
            <genre>Drama</genre>
            <genre>Musical</genre>
            <reviews>
                <atom:link rel="http://example.org/relations/reviews"
                  href="http://example.org/reviews"
                  type="application/atom+xml"
                  href="http://example.org/reviews?start=1&count=10"/>
                <review>
                    <summary>Blah blah</summary>
                    <link rel="self"
                      href="http://example.org/review/1234"/>
                    <link rel="http://example.org/relations/reviewer"
                      href="http://example.org/reviewer/1"/>
                </review>
                <review>
                    <summary>Blah blah blah blah</summary>
                    <link rel="self"
                      href="http://example.org/review/5678"/>
                    <link rel="http://example.org/relations/reviewer"
                      href="http://example.org/reviewer/2"/>
                </review>
            </reviews>
        </movie>
    </atom:content>
</atom:entry>

Here the server included summaries of two reviews each of which containing a link to the reviewer. It also included a link to fetch all reviews. It is possible to simplify this by just linking to a reviews feed.

<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"
            xmlns:xml="http://www.w3.org/XML/1998/namespace">
    <atom:author>
        <atom:name>Blah blah </atom:name>
    </atom:author>
    <atom:title>Fiddler on the Roof</atom:title>
    <atom:link rel="self" href="http://example.org/movie/1234"/>
    <atom:link rel="http://example.org/relations/reviews" type="application/atom+xml"
    <atom:id>urn:movies:movie:1234</atom:id>
    <atom:published>2007-09-06T00:00:00Z</atom:published>
    <atom:updated>2007-09-06T00:00:00Z</atom:updated>
    <atom:content xml:lang="en-US" atom:type="application/vnd.exaple.movies+xml">
        <movie xmlns:atom="http://www.w3.org/2005/Atom">
            <title>Fiddler on the Roof</title>
            <year>1971</year>
            <duration>181</duration>
            <genre>Drama</genre>
            <genre>Musical</genre>
        </movie>
    </atom:content>
</atom:entry>

But ask anyone developing a UI, and they will ask you to include all the data they need in a single response.

For Other XML Family of Representations

I would recommend using the atom:link element for non-Atom representations as well since it is comprehensive enough. An alternative is to use XHTML's link element as defined in the XHTML Link Module. However, there is one key difference. XHTML requires that custom link relations to be declared in a profile linked from the head element of an XHTML document, where as Atom lets you use URIs as link relations. I find Atom's approach more flexible.

For JSON Representations

In one of his early posts on RESTful JSON, Joe Gregorio suggests properties like href, next etc with values as relative or absolute URIs. But I don's find that extensible.

Here is a comprehensive suggestion. Use a property named link (where is just one link) or links (when there is more than link) whose value is an object or an array of objects, with each object containing a set with the following properties.

  • href: URI of the link.
  • rel: Link relation
  • type (optional): Media type of the representation identified by the URI
  • hreflang (optional): Content language of the representation identified by the URI
  • title (optional): Human readable information about the link
  • length (optional): Content length of the representation identified by the URI

This merely maps Atom's link element to JSON so that we can use the same set of relations and the relationship extensibility model.

Here is an example.

{
  "movie" : {
    "link" : {
       "rel" : "self",
       "href" : "http://example.org/movie/11",
     },
    "title" : "Fiddler on the Roof",
    "year" : "1971",
    "duration" : 181,
    "genre" : ["Drama", "Musical"],
    "reviews" : {
       "links" : [
         {
           "rel": "related review",
           "href" : "http://example.org/review/1001",
         },
         {
           "rel": "related review",
           "href" : "http://example.org/review/1002",
         }
       ]
    }
  }
}

This suggestion may be a bit verbose than using, say, an href property, but is much more extensible.

{ 5 comments… read them below or add one }

1 Bill Burke October 11, 2008 at 1:49 pm

Why not use something like Badger which defines a mapping between JSON and XML?

http://badgerfish.ning.com/

Its a little extra verbosey, but its good enough IMO. Then you don’t have to rework all these XML formats within JSON.

For example, we integrated the Jettison project into JBoss Resteasy (JAX-RS). Jettison allows you to (un)marshall JSON to and from JAXB annotated classes. They have the Badger format, as well as a less-verbose mapping format (which we use by default).

All this atom talk got me excited, so I’m implementing a JAXB model for Atom. I’m going to add some hooks into JAX-RS so you can easily inject links that are derived from the base URIs of the incoming request…

Reply

2 subbu October 11, 2008 at 5:47 pm

All this atom talk got me excited, so I’m implementing a JAXB model for Atom. I’m going to add some hooks into JAX-RS so you can easily inject links that are derived from the base URIs of the incoming request…

Have you looked at integrating Rome or Abdera? One problem with JAXB is that it does not support RNG, and RNG is more flexible when it comes it unordered children. Specifying a native Java API for Atom may be easier than JAXB.

Reply

3 Jamie Kirkpatrick November 19, 2009 at 11:20 pm

Great post Subbu: I read your other article (http://www.infoq.com/articles/subbu-allamaraju-rest) regarding the use of links to navigate between resources. I immediately started pondering how this translated to JSON if that format was used instead: this post does a good job of trying to provide a solution. Good work!

Reply

Leave a Comment

{ 1 trackback }

Previous post:

Next post: