REST to Objects in C#
RESTful interfaces for web services are all the rage for many Web 2.0 sites. If you want to consume these in a very simple fashion, LINQ to XML can do the job pretty easily in C#. If you go searching for help on this, you’ll find a lot of incomplete solutions and fairly large toolkits and frameworks (guess how I know this) – this quick article is meant to be a no fluff just stuff approach to making this work.
POCO Objects
Let’s assume you have a Model that you want to suck data into from a RESTful web service. Ideally this is a Plain Old CLR Object, meaning it isn’t infected with any persistence or serialization goop. It might look something like this:
public class Entry
{
public int Id;
public int UserId;
public DateTime Date;
public float Hours;
public string Notes;
public bool Billable;
public override string ToString()
{
return String.Format("[{0}] User: {1} Date: {2} Hours: {3} Notes: {4} Billable {5}", Id, UserId, Date, Hours,
Notes, Billable);
}
}
Not that this isn’t a completely trivial object. Let’s look at the API for the service.
RESTful HTTP Service
In this case, it’s TickSpot’s API, with the following sample output:
<?xml version="1.0" encoding="UTF-8"?>
<entries type="array">
<entry>
<id type="integer">24</id>
<task_id type="integer">14</task_id>
<user_id type="integer">3</user_id>
<date type="date">2008-03-08</date>
<hours type="float">1.00</hours>
<notes>Had trouble with tribbles.</notes>
<billable>true</billable> # Billable is an attribute inherited from the task
<billed>true</billed> # Billed is an attribute to track whether the entry has been invoiced
<created_at type="datetime">Tue, 07 Oct 2008 14:46:16 -0400</created_at>
<updated_at type="datetime">Tue, 07 Oct 2008 14:46:16 -0400</updated_at>
# The following attributes are derived and provided for informational purposes:
<user_email>scotty@enterprise.com</user_email>
<task_name>Remove converter assembly</task_name>
<sum_hours type="float">2.00</sum_hours>
<budget type="float">10.00</budget>
<project_name>Realign dilithium crystals</project_name>
<client_name>Starfleet Command</client_name>
</entry>
</entries>
I’m assuming in this case that I don’t necessarily care about all of the data fields the service is returning – I just need some of them for my application’s purposes. Thus, you can see there are more elements in the <entry> XML than I have in my Entry class.
Get The XML with C#
The next step is to get the XML. The following snippet does the heavy lifting once you pass it the appropriate URL:
protected XElement GetResponse(string uri)
{
var request = WebRequest.Create(uri) as HttpWebRequest;
request.UserAgent = ".NET Sample";
request.KeepAlive = false;
request.Timeout = 15 * 1000;
var response = request.GetResponse() as HttpWebResponse;
if (request.HaveResponse == true && response != null)
{
var reader = new StreamReader(response.GetResponseStream());
return XElement.Parse(reader.ReadToEnd());
}
throw new Exception("Error fetching data.");
}
This is adapted from the Yahoo Developer article on Web Service REST calls. Once you have the XML, the last step is to get the data back as your POCO.
Use LINQ-To-XML to Deserialize POCOs from XML
This is done via the following code:
public IEnumerable<Entry> List(DateTime startDate, DateTime endDate)
{
string additionalParameters =
String.Format("start_date={0}&end_date={1}",
startDate.ToShortDateString(),
endDate.ToShortDateString());
string uri = BuildUrl("entries", additionalParameters);
XElement elements = GetResponse(uri);
var entries = from e in elements.Elements()
where e.Name.LocalName == "entry"
select new Entry
{
Id = int.Parse(e.Element("id").Value),
UserId = int.Parse(e.Element("user_id").Value),
Date = DateTime.Parse(e.Element("date").Value),
Hours = float.Parse(e.Element("hours").Value),
Notes = e.Element("notes").Value,
Billable = bool.Parse(e.Element("billable").Value)
};
return entries;
}
For completeness, here’s the BuildUrl method for my TickSpot API wrapper:
// Change these to your settings
protected const string projectDomain = "DOMAIN.tickspot.com";
private const string authParams = "email=johndoe@domain.com&password=MyTickSpotPassword";
protected string BuildUrl(string apiMethod, string additionalParams)
{
if (projectDomain.Contains("DOMAIN"))
{
throw new ApplicationException("You must update your domain in ProjectRepository.cs.");
}
if (authParams.Contains("MyTickSpotPassword"))
{
throw new ApplicationException("You must update your email and password in ProjectRepository.cs.");
}
return string.Format("https://{0}/api/{1}?{2}&{3}", projectDomain, apiMethod, authParams, additionalParams);
}
That’s it! Now go forth and consume XML and map it to classes you actually want to work with. Have fun!