Kian Ryan ([info]kianryan) wrote,
@ 2008-03-29 02:17:00
Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Talking to Flickr

I like Flickr. Flickr is my friend, I use it to post my own photos, browse those of my friends, and to play around with groups. There are some awesome groups on Flickr, such as Strobist, BlackandWhite and ILoveFilm (I'm also a member of similar communities on Livejournal, can you guess what I do for kicks?).

One of the tasks I seem perform quite regularly is to talk to (read query) Flickr. Flickr provides a wide number of ways to query their service, including XML-RPC, SOAP and REST. XML-RPC isn't native to .NET (and Flickr doesn't work properly with XML-RPC.NET, there's no WSDL which makes SOAP awkward which leaves REST. The elegance of REST is in it's simplicity; you give it a URL, it gives you an XML file. Couldn't be simpler.

Well, relatively simple anyway. Prior to .NET 3.0, you would need to create your object (which was rather long-winded), fetch the object (HTTPRequest anyone?), load it into XDocument, iterate over the nodes, create the node to object mapping and add to a collection. Or use a strongly typed DataSet, but really, this is 2008.

And then do something with it.

Then .NET 3 and C# 3 came along. Anonymous methods, anonymous types. Humm... Monsieur Microsoft, you spoil us! All this stuff is leading towards Microsoft's next big thing, LINQ (Language Integrated Query). A nice system that allows you to query any IEnuerable or IQueryable collection with a shiny syntax. How that's going to fit in with some of the existing query frameworks including NHibernate.Query and Subsonic.Query we will have to wait and see, but for now, let's get something useful from Flickr.

Flickr REST requests take the form of a url string. The one below executes the method groups.pools.getPhotos (gets photos from a given Flickr Pool) for the group NorthWestFencing.

http://api.flickr.com/services/rest/?method=flickr.groups.pools.getPhotos& _

api_key=e03402abb5a6f36bddcae3ba9c2aaf98&group_id=697314@N21

Forming the url is left as an exercise to the reader, but the object cheat sheet is here. If you've not seen it before, have a look at the ToString() method, more anonymous delegates in there! The example above returns an XML document containing a number of <photo> elements. An example response is given below:

<rsp stat="ok">
    <photos page="1" pages="1" perpage="100" total="1">
        <photo id="2308416535" owner="21037076@N08" secret="08cb209e0a" 
            server="3274" farm="4" title="Sheffield Open 2006" ispublic="1" 
            isfriend="0" isfamily="0" ownername="kianryan" dateadded="1206480190" />
    </photos>
</rsp>

Which is in fact, one of my own photos from the 2006 Sheffield Open. Argh, that's alot of fields to map! Now we need an object to map these photos to. And here again, C# 3 comes to the rescue (of sorts). With previous versions of C#, you would have had to spend quite a bit of tedious time declaring private fields and public accessors for each property you wanted to expose. The new shorthand get; set; format creates a public accessor and default sets and gets for the property. So much time saved. So the following:

private int  _Id;
public int Id {
    get {
        return _Id;
    }
    set{
        _id = value;
    }
}

becomes:

public int Id {
    get;
    set;
}

9 lines of code to 4. And I'm being nice with the 9 there, I'd personally drop those opening curly brackets onto the next line. Cleaner and more code snippet friendly to.

So now we have our object declared (in half the lines and half the time it used to take) and we can get to the clever bit. The namespace System.Xml.Linq provides the XElement object, exposing the static Load element, which returns an XElement object containing a tree of XElements describing the document. Got that? Okay, have a look here then. As a little nicety, the Load method will take a url which means we get to avoid all that tedious prefetching. Of course, for anything production level, we need to look at caching, but we'll ignore that for now.

We can now return a List of Photo objects using the following code (don't forget to include the System.Linq and System.Xml.Linq namespace:

var query = from photo in XElement.Load(url).Descendants("photo")
    select new Photo
    {
        Id = long.Parse(photo.Attribute("id").Value),
        FarmId = long.Parse(photo.Attribute("farm").Value),
        Owner = photo.Attribute("owner").Value,
        Title = photo.Attribute("title").Value,
        Secret = photo.Attribute("secret").Value,
        Server = long.Parse(photo.Attribute("server").Value),
        IsPublic = Convert.ToBoolean(byte.Parse(photo.Attribute("ispublic").Value)),
        IsFriend = Convert.ToBoolean(byte.Parse(photo.Attribute("isfriend").Value)),
        IsFamily = Convert.ToBoolean(byte.Parse(photo.Attribute("isfamily").Value)),
        Ownername = photo.Attribute("ownername").Value
    };

return query.ToList<photo>();

This code builds an object of an anonymous type called query, which will iterate over the elements called <photo> in the xml document, and for each element select an object of type Photo with all the appropriate fields mapped. If you think about it, that's alot of work for the number of lines involved. And that's just a straight select all. We can also perform wheres, joins and a full range of query tools. More information on Linq is availible on Mike Taulty's Blog

The last line causes the query to execute and returns a List of Photos. In my implementation of Photo, I provide additional properties to return the urls for the returned photos such as:

private string SmallUrl
{ 
    get{
        return string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}_{4}.jpg",
            FarmId, Server, Id, Secret, 's');
    }
}

which we can bind directly in ASP.NET, Silverlight, Windows Forms, etc. A very powerful and flexible solution which also gives us nice levels of seperation between the request, response and presentation. It's also surprisingly straightforward, takes very few objects to perform and is highly reusable.




(Post a new comment)

Even shorter...
(Anonymous)
2008-03-31 01:04 am UTC (link)
You can make the code even shorter by using the explicit cast operators defined by XElement. For example,

var query = ...
select new Photo {
Id = (long)photo.Attribute("id"),
FarmId = (long)photo.Attribute("farm"),
Owner = (string)photo.Attribute("owner"),
...
};

(Reply to this) (Thread)

Re: Even shorter...
[info]pteppic
2008-09-26 12:54 am UTC (link)
Ah, you star. I'll give this a try next time around.

(Reply to this) (Parent)


Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…