Saturday, August 28, 2010

Using a Google Map as a database – Part 2

Splitting the ATOM
Yeah, sorry about that. Democritus claimed that ATOM was indivisible, the smallest piece of matter there was. But using the new SyndicationFeed API, we can easily parse the ATOM feed into its component parts, breaking each individual entry (corresponding to a hotel, or guesthouse, in my accommodation example from Part 1) into its title, date published, summary, etc.

Now these properties of the entry are not where our data payload is: it's in the <content> tag. That's just where Google places the info-window markup. And that's where you'd want to put it if you were generating the feed yourself. Unless you're simply returning a collection of items with properties corresponding to a title, LastUpdatedTime, PublishDate and the like, you want to resist the urge to shoehorn your data into one of those slots. If your datastore is a pair of really big feet, then these properties are ladies' shoes. Small ladies. And sure, it may feel fantastic to flounce around in them for a while, but it'll end in fear and shame, trust me.

Unpacking the content from the description node in the KML is a little tricky, and if anyone knows a better way of doing this, please let me know. For a start, it's wrapped in XML comments. Second, as I mentioned earlier, because of the restrictions within the Google Map UI, you can't easily make it XML-compliant. In any case, it's passed through as unstructured. The fact that it happens to be sort of structured just makes it a little bit easier for me at the unpacking end, but it's not at all necessary. So, I simply pass it through a sanitising method, closing the img tags, reinstating the angle brackets, ending up with an XElement.

But deserializing the individual feed entries is much easier, despite the fact that they seem to be wrapped in up ATOM feed entries, and would seem to need to be tackled using LINQ2XML, or XPath, or some XML API. Using the new HttpClient to get the response,
feedUrl = "http://www.connemara.net/Services/AccommodationMap/Feed.svc/type/" + type;
var client = new HttpClient(feedUrl);
var response = client.Get();
response.EnsureStatusIsSuccessful();
var MapAtomFeed = response.Content.ReadAsSyndicationFeed();
we simply run through the feed entries, and deserialize them into our domain objects:
var providers = new Providers();
foreach (var item in MapAtomFeed.Items) {
    var provider = (item.Content as XmlSyndicationContent).ReadContent<Provider>;
    providers.Add(provider);
}
There's the goodness! ReadContent of type Provider. I can automatically turn that ATOM feed entry back into my (Accommodation) Provider domain object! The key to this lies in the namespace accompanying each feed entry's Provider node, which tells .Net how to unpack the entry.

ATOM screenshot

See it there? The
<Provider xmlns="http://schemas.datacontract.org/2004/07/ConnemaraNet.Services.AccommodationMap" ... >
part? That last section - ConnemaraNet.Services.AccommodationMap - is the namespace of my original datacontract. Once that's recreated on the client side of the service, you're in business.

And that's it. Here's the map that I use as my database. And here's my accommodation page, using that map.