Parsing Last.fm Web Services' artist.getSimilar with C# and LINQ to XML
The following covers how to parse the XML response of artist.getSimilar, from Last.fm's Web Services.
Setup and assumptions
The first step is sign up for a free API account at Last.fm.
You'll also need to target .NET Framework 3.5 when you setup your project, so as to access LINQ functionality.
When writing the steps listed below, I was working on a Windows Forms Application, but the steps should be the same, or very similar, for other project types.
Creating the base class
The first thing I've done is created a new class file in my project called Lastfm.cs, resulting in the following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace JamesRSkemp.WebServices {
class Lastfm {
private const string LastFmApiKey = “EnterYourApiKeyHere”;
}
}
We'll add a new method to the Lastfm class to return the base Url we'll need to make Web service requests.
/// <summary>
/// Get the base Url that we'll use to make Web service requests.
/// </summary>
/// <returns>The base Url to use to make Web service requests.</returns>
static private string GetBaseRequestUrl() {
string baseUrl = "http://ws.audioscrobbler.com/2.0/?api_key=" + LastFmApiKey;
return baseUrl;
}
Next we'll create a method to make a request to a Web service and return the full response.
/// <summary>
/// Gets the data from an HTTP request.
/// </summary>
/// <param name="requestUrl">The full Url of the request to make.</param>
/// <returns>Returns a string with the text returned from the request.</returns>
private string GetServiceResponse(string requestUrl) {
string httpResponse = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
request.Timeout = 15000;
HttpWebResponse response = null;
StreamReader reader = null;
try {
response = (HttpWebResponse)request.GetResponse();
reader = new StreamReader(response.GetResponseStream());
httpResponse = reader.ReadToEnd();
} finally {
if (reader != null) {
reader.Close();
}
if (response != null) {
response.Close();
}
}
return httpResponse;
}</code></pre>
The following references must also be added.
using System.Net;
using System.IO;
When we use this class, we want to have our data returned in an easy to use format. For ease, we'll have it return a DataTable. We'll have to add the appropriate reference first.
using System.Data;
We can then began our method as follows.
public DataTable GetSimilarArtists(string artistName) {
if (String.IsNullOrEmpty(artistName)) {
throw new Exception("Artist name must be populated.");
} else {
string requestUrl = GetBaseRequestUrl();
requestUrl += "&method=artist.getSimilar&artist=" + System.Web.HttpUtility.UrlEncode(artistName.Trim());
string serviceResponse = GetServiceResponse(requestUrl);
DataTable similarArtists = new DataTable();
// TODO
return similarArtists;
}
}</code></pre>
If we were to make a request now, we'd see that the data returned is formatted similar to the following, for a request for Bruce Springsteen. (For ease and sanity, data truncated.)
<?xml version="1.0" encoding="utf-8"?>
<lfm status="ok">
<similarartists artist="Bruce Springsteen">
<artist>
<name>Bruce Springsteen & The E Street Band</name>
<mbid>5a1283bf-81d5-4700-8919-683eeaaf2beb</mbid>
<match>100</match>
<url>www.last.fm/music/Bruce%2BSpringsteen%2B%2526%2BThe%2BE%2BStreet%2BBand</url>
<image size="small">http://userserve-ak.last.fm/serve/34/8415485.jpg</image>
<image size="medium">http://userserve-ak.last.fm/serve/64/8415485.jpg</image>
<image size="large">http://userserve-ak.last.fm/serve/126/8415485.jpg</image>
<image size="extralarge">http://userserve-ak.last.fm/serve/252/8415485.jpg</image>
<image size="mega">http://userserve-ak.last.fm/serve/500/8415485/Bruce+Springsteen++The+E+Street+Band+estreet.jpg</image>
<streamable>1</streamable>
</artist>
[...]
</similarartists></lfm>
We'd first need to add a reference so that we can parse through the returned response.
using System.Xml.Linq;
We can now create our LINQ to XML query to access the similar artist's name and match percentage.
var xmlResponse = XElement.Parse(serviceResponse);
// Parse through the returned Xml for the name and match value for each similar artist.
var artists = from artistsSimilar in xmlResponse.Descendants("artist")
select new {
name = artistsSimilar.Element("name").Value,
match = artistsSimilar.Element("match").Value
};</code></pre>
Next we can create the DataTable that we'll use to store the name and math values.
DataTable similarArtists = new DataTable();
similarArtists.Columns.Add("Artist");
similarArtists.Columns.Add("Match", System.Type.GetType("System.Double"));
Finally we can loop through each result returned from our LINQ to XML query, adding a new row to the table, for each.
if (artists.Count() > 0) {
DataRow artistsRow;
foreach (var artist in artists) {
artistsRow = similarArtists.NewRow();
artistsRow["Artist"] = artist.name;
artistsRow["Match"] = artist.match;
similarArtists.Rows.Add(artistsRow);
}
}
Finally we return the populated DataTable.
return similarArtists;
Assuming a Windows Form Application with a text box (textBox1), a DataGridView (dataGridView1), and a button (button1), we could do the following (assuming the appropriate references have been added).
private void button1_Click(object sender, EventArgs e) {
if (!String.IsNullOrEmpty(textBox1.Text)) {
Lastfm lastFmRequest = new Lastfm();
DataTable results = lastFmRequest.GetSimilarArtists(textBox1.Text);
dataGridView1.DataSource = results;
} else {
MessageBox.Show("You must enter an artist to continue.");
textBox1.Focus();
}
}</code></pre>
Taken together, that results in the following. (Download JamesRSkemp.WebServices.Lastfm.cs.)
/*
Created by James Skemp - http://jamesrskemp.com/
Version 1.0
More information at http://strivinglife.com/words/post/Parsing-Lastfm-Web-Services-artistgetSimilar-with-C-and-LINQ-to-XML.aspx
Shared under a Creative Commons Attribution 3.0 United States License - http://creativecommons.org/licenses/by/3.0/us/
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Data;
using System.Xml.Linq;
namespace JamesRSkemp.WebServices {
class Lastfm {
/// <summary>
/// Key used to access Last.fm Web services.
/// </summary>
private const string LastFmApiKey = “EnterYourApiKeyHere”;
/// <summary>
/// Return artists similar to the one passed, with a match percentage.
/// </summary>
/// <param name="artistName">The name of the artist to use for the request.</param>
/// <returns>DataTable with artist names and match percentage, as a Double.</returns>
public DataTable GetSimilarArtists(string artistName) {
if (String.IsNullOrEmpty(artistName)) {
throw new Exception("Artist name must be populated.");
} else {
string requestUrl = GetBaseRequestUrl();
requestUrl += "&method=artist.getSimilar&artist=" + System.Web.HttpUtility.UrlEncode(artistName.Trim());
string serviceResponse = GetServiceResponse(requestUrl);
var xmlResponse = XElement.Parse(serviceResponse);
// Parse through the returned Xml for the name and match value for each similar artist.
var artists = from artistsSimilar in xmlResponse.Descendants("artist")
select new {
name = artistsSimilar.Element("name").Value,
match = artistsSimilar.Element("match").Value
};
DataTable similarArtists = new DataTable();
similarArtists.Columns.Add("Artist");
similarArtists.Columns.Add("Match", System.Type.GetType("System.Double"));
if (artists.Count() > 0) {
DataRow artistsRow;
foreach (var artist in artists) {
artistsRow = similarArtists.NewRow();
artistsRow["Artist"] = artist.name;
artistsRow["Match"] = artist.match;
similarArtists.Rows.Add(artistsRow);
}
}
return similarArtists;
}
}
/// <summary>
/// Get the base Url that we'll use to make Web service requests.
/// </summary>
/// <returns>The base Url to use to make Web service requests.</returns>
private string GetBaseRequestUrl() {
string baseUrl = "http://ws.audioscrobbler.com/2.0/?api_key=" + LastFmApiKey;
return baseUrl;
}
/// <summary>
/// Gets the data from an HTTP request.
/// </summary>
/// <param name="requestUrl">The full Url of the request to make.</param>
/// <returns>Returns a string with the text returned from the request.</returns>
private string GetServiceResponse(string requestUrl) {
string httpResponse = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
request.Timeout = 15000;
HttpWebResponse response = null;
StreamReader reader = null;
try {
response = (HttpWebResponse)request.GetResponse();
reader = new StreamReader(response.GetResponseStream());
httpResponse = reader.ReadToEnd();
} finally {
if (reader != null) {
reader.Close();
}
if (response != null) {
response.Close();
}
}
return httpResponse;
}
}
}
Advanced error handling is missing, but this should give you a basic idea of how you can go about easily accessing Last.fm's Web Services, and parsing returned data.
Final thoughts
Questions? Comments? Concerns? Please add a comment below.
Updated 9/12/2009, 15:55: I was doing it in my implementation, but added simple check for a populated textBox1 to the above code.
Search
Links of Note
Support This Site
If my blog was helpful to you, then please consider visiting my Amazon Wishlist.