JavaScript appears to be disabled. We recommend you enable JavaScript while visiting this site.

(All original content on this site is licensed under the Creative Commons License Attribution-Noncommercial-No Derivative Works 3.0.)

Determine BlogEngine.NET comments that haven't been published

Unfortunately, BlogEngine.NET doesn't currently have a very good way to determine, at a glance, all of the comments that haven't been approved. While this will certainly be coming in a future release, or as an extension, I figured writing something simple to do this would be a good LINQ to XML test for me.

You can download the built executable, or play with the code, which is included below.

Download the executable (7-Zip format). Requires .NET Framework 3.5.

The form I created consisted of a TextBox, Button, and a DataGridView, with the default names.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;

namespace TestBlogEngine {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e) {
			OpenFileDialog sampleFile = new OpenFileDialog();
			sampleFile.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";

			if (sampleFile.ShowDialog() == DialogResult.OK) {
				textBox1.Text = sampleFile.FileName;

				string postsDirectory = System.IO.Path.GetDirectoryName(sampleFile.FileName);
				sampleFile.Dispose();

				string[] postFiles = System.IO.Directory.GetFiles(postsDirectory);

				DataTable comments = new DataTable();
				comments.Columns.Add("Post");
				comments.Columns.Add("CommentApproved");
				comments.Columns.Add("FileId");

				XDocument postXml;

				foreach (string postFile in postFiles) {
					postXml = XDocument.Load(postFile);

					var posts = from postData in postXml.Descendants("post")
						select new {
							Title = postData.Element("title").Value,
							CommentItems = (from commentItems in postData.Element("comments").Elements("comment")
								select commentItems).ToList()
						};

					foreach (var post in posts) {
						if (post.CommentItems.Count > 0) {
							foreach (var commentItem in post.CommentItems) {
								if (commentItem.Attribute("approved") != null && commentItem.Attribute("approved").Value == "False") {
									DataRow comment = comments.NewRow();
									comment["Post"] = post.Title;
									comment["CommentApproved"] = commentItem.Attribute("approved").Value;
									comment["FileId"] = "/post.aspx?id=" + System.IO.Path.GetFileNameWithoutExtension(postFile);
									comments.Rows.Add(comment);
								}
							}

						}
					}
				}

				postXml = null;

				dataGridView1.DataSource = comments;

			}
		}
	}
}

EDIT: Scott Guthrie's excellent Using LINQ to XML (and how to build a custom RSS Feed Reader with it) is the article that I keep going back to when I forget LINQ to XML basics.

(All original content on this site is licensed under the Creative Commons License Attribution-Noncommercial-No Derivative Works 3.0.)

Parsing Yahoo! Music's Artist Web Services with C# and LINQ to XML - Search for artists

Similar to my post on parsing Last.fm's artist.getSimilar, I've been working with Yahoo! Music's Web services today.

Unfortunately, Yahoo!'s services aren't quite as friendly as those made available by Last.fm.

So that I remember, and others don't have to tackle this as well, here's the class I've created. (Download JamesRSkemp.WebServices.YahooMusic.cs.)

/*
Created by James Skemp - http://jamesrskemp.com/
Version 1.0
More information at http://strivinglife.com/words/post/Parsing-Yahoo!-Musics-Artist-Web-Services-with-C-and-LINQ-to-XML-Search-for-artists.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.Xml.Linq;
using System.Data;

namespace JamesRSkemp.WebServices {
	class YahooMusic {
		/// <summary>
		/// Key used to access Yahoo! Music Web services.
		/// </summary>
		private string AppId = "";

		/// <summary>
		/// Create a new YahooMusic object.
		/// </summary>
		/// <param name="appId">Application ID from Yahoo! Developer Network.</param>
		public YahooMusic(string appId) {
			if (appId.Trim() != "") {
				AppId = appId;
			} else {
				throw new Exception("You must pass a valid API identifier.");
			}
		}

		/// <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.</returns>
		public DataTable GetSimilarArtists(string artistName) {

			string requestUrl = "http://us.music.yahooapis.com/artist/v1/list/search/artist/"
				+ System.Web.HttpUtility.UrlEncode(artistName.Trim())
				+ "?appid=" + AppId + "&response=topsimilar";

			string serviceResponse = GetServiceResponse(requestUrl);

			var xmlResponse = XElement.Parse(serviceResponse);

			var artistsCount = from Artists in xmlResponse.Descendants("TopSimilarArtists").Descendants("Artist")
							   select new {
								   name = Artists.Attribute("name").Value
							   };

			DataTable similarArtists = new DataTable();
			similarArtists.Columns.Add("Artist");

			if (artistsCount.Count() > 0) {
				DataRow artistsRow;

				foreach (var artist in artistsCount) {
					artistsRow = similarArtists.NewRow();
					artistsRow["Artist"] = artist.name;
					similarArtists.Rows.Add(artistsRow);
				}
			}

			return similarArtists;
		}

		/// <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;
		}
	}
}

One issue I do have is that I can't figure out how to get the count attribute off the root element that's returned. Try and search as I might, I can't figure it out.

(All original content on this site is licensed under the Creative Commons License Attribution-Noncommercial-No Derivative Works 3.0.)

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;
		}

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;
			}
		}

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
							  };

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();
			}
		}

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.

Month List