Introduction
In this week's companion article on the Radar blog, I discuss how principles of common-sense information architecture govern whether public events posted to Facebook will or won't be found by search. Here I'll show how the elmcity service, which recently added Facebook to its set of event sources, uses the Facebook search API.
Encapsulating Facebook's search API
This method wraps a call to the Facebook API. The RetryExpectingOK method uses the retry logic I developed in How to retry generically in C#.
public string CallFacebookApi(string method, string args)
{
// http://graph.facebook.com/search?q={QUERY}&type=event&access_token=xxx
try
{
var key = this.apikeys.facebook_api_key;
string host = "https://graph.facebook.com";
string url = string.Format("{0}/{1}?access_token={2}&type=event&{3}",
host, method, key, args);
GenUtils.LogMsg("info", url, null);
var request = (HttpWebRequest)WebRequest.Create(new Uri(url));
var response = HttpUtils.RetryExpectingOK(request, data: null, wait_secs: this.wait_secs,
max_tries: this.max_retries, timeout_secs: this.timeout_secs);
return response.DataAsString();
}
catch (Exception e)
{
GenUtils.LogMsg("exception", "CallFacebookApi", e.Message + e.StackTrace);
return "";
}
}
Unpacking Facebook results
In How to search Google and Bing in C# I used a third-party library, Json.NET, to unpack JSON results from those two search engines. Here I'm using one of the .NET Framework's built-in deserializers, System.Web.Script.Serialization.JavascriptSerializer.
// using the built-in json deserializer here, in contrast to the 3rd party newtonsoft.json
public IEnumerable<FacebookEvent> FacebookIterator(string method, string args)
{
var json = CallFacebookApi(method, args);
var serializer = new System.Web.Script.Serialization.JavascriptSerializer();
var dict = (Dictionary<string, object>)serializer.DeserializeObject(json);
var items = (Object[])dict["data"];
foreach (Dictionary<string, object> item in items)
{
var name = (string)item["name"];
var location = (string)"";
try { location = (string)item["location"]; }
catch { };
var start_time = (string)item["start_time"];
var id = (string)item["id"];
yield return new FacebookEvent(name, location, start_time, id);
}
}
What's the difference between the two? Consider this JSON fragment from Facebook:
{
"data":[
{
"name":"Tiff Jimber in Keene, NH",
"start_time":"2010-11-20T03:00:00+0000",
"end_time":"2010-11-20T06:00:00+0000",
"location":"Fritz's Place",
"id":"151905824833190"
},
{
"name":"World History @ Starving Artist (Keene NH)",
"start_time":"2010-11-15T04:00:00+0000",
"end_time":"2010-11-15T07:30:00+0000",
"location":"The Starving Artist",
"id":"100599506662874"
},
{
"name":"Opening of Waxy Oconnor's at Best Western keene, NH",
"start_time":"2010-11-05T18:00:00+0000",
"end_time":"2010-11-05T19:00:00+0000",
"location":"Best Western Keene, NH",
"id":"138781362811592"
}
],
"paging":{
"previous":"https://graph.facebook.com/search?access_token=xxx&type=event&q=keene%2Cnh&since=2011-01-01T04%3A00%3A00%2B0000&limit=1000",
"next":"https://graph.facebook.com/search?access_token=xxx&type=event&q=keene%2Cnh&limit=1000&until=2010-11-05T17%3A59%3A59%2B0000"
}
}
Let's load this fragment using both JSON deserializers and compare access styles:
var s = File.ReadAllText("fb.json");
var j_obj = (JObject)JsonConvert.DeserializeObject(s);
var serializer = new System.Web.Script.Serialization.JavascriptSerializer();
var n_obj = serializer.DeserializeObject(s);
Using the native .NET object, this expression will yield the first title, "Tiff Jimber in Keene, NH":
( ( Dictionary<string,object>) ( (Object[]) ( (Dictionary<string,object>) n_obj )["data"] ).First() )["name"]
Here's the equivalent -- and clearly more concise -- Json.NET expression:
j_obj["data"].First()["name"].Value<string>()
Json.NET also supports LINQ, you can gather the results like so:
var results_query =
from result in j_obj["data"].Children()
select new FacebookEvent(
name: result.Value<string>("name").ToString(),
location: result.Value<string>("location").ToString(),
start_time: result.Value<string>("start_time").ToString(),
id: result.Value<string>("id").ToString()
);
I generally prefer Json.NET, but it does create an extra dependency. In the end, it's not that much harder to use the built-in deserializer. Either way, I always have to do a bit of head-scratching to figure out how to navigate the graphs produced by these deserializers and how to cast their objects to .NET types. That's part of the reason I write these answers: So I can look them up myself!

Help





