Linq to XML Example (Parsing RSS XML)

[Discalimer]
This is just fun code and nothing too serious.  Anything bigger than this should be hosted on a site where others can contribute.

[The Story]
A while back, someone asked a question on Twitter concerning LINQ to XML.  I made a small example then as a proof of concept, then i kept playing with it until I came up with a lite framework for a utility that can be used to search and download DotNetRocks podcasts.

Right now, the utility is a stand-alone command-line app, but is written in such a way that it could be used in any type of app (graphical or other).

[What it does:]
The utility parses the XML of the RSS feed and allows searches by keyword into the title and description of the podcasts.
When the proper ones are found, they can be downloaded (into the "temp" directory).

Here is the feed it parses:
http://www.pwop.com/feed.aspx?show=dotnetrocks&filetype=master#

The "meat" of the program is in the function: GetMp3ListBySubject();


// DownloadDnr.cs
////////////////////////////////////////////////////////////////////////////////
// This program downloads content from the DotNetRocks repository based on the
// given search criteria.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using System.Resources;

namespace DownloadDnr
{
   partial class CDownloadDnr
   {
      static string m_strUsage = "DownloadDnr {theme} {auto}\n"
               + "\twhere {theme} is the search word for the"
               + " show(s) to be downloaded.\n\n"
               + "[Example:]\n\tDownloadDnr \"kate gregory\" auto\n"
               + "\twill list and download all shows mentioning\n"
               + "\tKate Gregory into the %temp% directory automatically.\n"
               + "\twithout asking the user.";

      static int m_intNumFilesProcessed = 0;
     
      static void Main(string[] args)
      {
         string strError = "";

         Func<string, bool> isArg =
            s => args.Skip(1).Any(s2 => s2.ToUpper().Equals(s));

         if (args.Length.Equals(0))
         {
            Console.WriteLine(m_strUsage);
            return;
         }
        
         List<CDnrResponse> lst = new List<CDnrResponse>();

         if (!GetMp3ListBySubject(args[0], ref lst, ref strError))
         {
            Console.WriteLine("Error getting list: " + strError);
            return;
         }

         if (lst.Count.Equals(0))
         {
            Console.WriteLine("Not found");
            return;
         }

         lst.ForEach(s => Console.WriteLine(s.ToString()));

         if (isArg("CLEAN"))
         {
            Console.WriteLine("Deleting: ");

            m_intNumFilesProcessed = 0;
            foreach (bool blnDeleted in DoClean(lst))
            {
               Console.WriteLine("File {0:G} deleted : " + lst[m_intNumFilesProcessed - 1],
                  (blnDeleted ? "has been" : "NOT found or"));
            }

            return;
         }

         if (!isArg("AUTO"))
         {
            if ('N'.Equals(ConsoleAskYesNo("Download this content? [Y/n]")))
            {
               Console.WriteLine("\n...aborted...");
               return;
            }
         }
         else
         {
            Console.Write("Auto ");
         }

         Console.WriteLine("Downloading:");

         ///////////////////////////////////////////////////////////////////////
         // Display the progress
         foreach (bool blnDownloaded in DoDownload(lst))
         {
            Console.WriteLine("{0:G}/{1:G} "
               + (blnDownloaded ? "Good: " : "Bad: ")
               + lst[m_intNumFilesProcessed-1],
               (m_intNumFilesProcessed), lst.Count);
         }
      }
   }
}


//ConsoleAskYesNo.cs
using System;
using System.Linq;

namespace DownloadDnr
{
   partial class CDownloadDnr
   {
      public static char ConsoleAskYesNo(string strQuestion)
      {
         char chrSelection = '\0';

         Func<char> GetUpperChar = delegate()
         { return Console.ReadKey().KeyChar.ToString().ToUpper()[0]; };

         string strValid = "NY\n";

         do
         {
            Console.WriteLine(strQuestion);
         } while (!strValid.Contains(chrSelection = GetUpperChar()));

         return chrSelection;
      }
   }
}

 


//DnrResponse.cs
namespace DownloadDnr
{
   public class CDnrResponse
   {
      public string strTitle
      {
         get;
         set;
      }

      public string strMp3
      {
         get;
         set;
      }

      public string strShowNum
      {
         get;
         set;
      }

      public CDnrResponse()
      {
         strTitle = "";
         strMp3 = "";
         strShowNum = "";
      }

      public CDnrResponse(CDnrResponse dnr)
      {
         strTitle = dnr.strTitle;
         strMp3 = dnr.strMp3;
         strShowNum = dnr.strShowNum;
      }

      public CDnrResponse(string strTitle, string strMp3, string strShowNum)
      {
         this.strTitle = strTitle.Trim();
         this.strMp3 = strMp3.Trim();
         this.strShowNum = strShowNum.Trim();
      }

      public override string ToString()
      {
         return strTitle + " (" + strShowNum + ")";
      }

      public override int GetHashCode()
      {
         return this.ToString().GetHashCode();
      }

      public override bool Equals(object obj)
      {
         return this.ToString().Equals(((CDnrResponse)obj).ToString());
      }
   }
}

 


//DoClean.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace DownloadDnr
{
   partial class CDownloadDnr
   {
      public static IEnumerable<bool> DoClean(List<CDnrResponse> lst_dnrFiles)
      {
         bool blnRetVal = true;
         string strTempPath = Path.GetTempPath();
         foreach (CDnrResponse dnrResp in lst_dnrFiles)
         {
            blnRetVal = true;

            try
            {
               string strTargetFile =
                  strTempPath
                  + dnrResp.strMp3.Split("\\/".ToCharArray()).Last();

               m_intNumFilesProcessed++;

               if (!File.Exists(strTargetFile))
               {
                  blnRetVal = false;
               }
               else
               {
                  File.Delete(strTargetFile);
               }
            }
            catch (Exception exc)
            {
               blnRetVal = false;
               System.Diagnostics.Trace.WriteLine(
                  "Delete Failed: " + exc.Message);
            }

            yield return blnRetVal;
         }
      }
   }
}

 


//DoDownload.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;

namespace DownloadDnr
{
   partial class CDownloadDnr
   {
      public static IEnumerable<bool> DoDownload(List<CDnrResponse> lst_dnrFiles)
      {
         bool blnRetVal = true;

         WebClient wc = new WebClient();
         string[] arr_strTemp = null;
         string strTempDir = Path.GetTempPath();
         //
         foreach (CDnrResponse dnrResp in lst_dnrFiles)
         {
            blnRetVal = true;
            arr_strTemp = dnrResp.strMp3.Split("\\/".ToCharArray());
            try
            {
               wc.DownloadFile(
                  new Uri(dnrResp.strMp3), strTempDir + arr_strTemp.Last());

               m_intNumFilesProcessed++;
            }
            catch (Exception exc)
            {
               blnRetVal = false;
               System.Diagnostics.Trace.WriteLine(
                  "Download Failed: " + exc.Message);
            }

            yield return blnRetVal;
         }
      }
   }
}

 


//GetMp3ListBySubject.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace DownloadDnr
{
   partial class CDownloadDnr
   {
      //////////////////////////////////////////////////////////////////////////
      // Return the list of MP3s containing titles or descriptions matching the
      // given criteria.
      static bool GetMp3ListBySubject(string strSubject,
         ref List<CDnrResponse> lst_dnrRetVal, ref string strError)
      {
         bool blnRetVal = true;
         //
         Func<XAttribute, string> UnUrl = s =>
            s.ToString().Replace("url=", "").Replace("\"", "");
         //
         Func<XElement, string> EpNum = s =>
            s.ToString().Split(
               "=<>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
               .Where(s2 => !s2.Equals("/guid")).ToArray().Last();
         //
         try
         {
            string strUpSubj = strSubject.ToUpper();
            /////////////////////////////////////////
            // Load the page as an XML document
            XDocument xdocDotNetRocks = XDocument.Load(
               "http://www.pwop.com/feed.aspx?show=dotnetrocks&filetype=master#"
               );

            ////////////////////////////////////////////////////////////////////
            // SELECT title, mp3, episodeNum FROM xdocDotNetRocks
            //  WHERE (UPCASE(title) LIKE '%LINQ%')
            //  OR (UPCASE(description) LIKE '%LINQ%')
            //  OR (UPCASE(guid) LIKE '%LINQ%');
            lst_dnrRetVal =
            (
               from XElement xe in xdocDotNetRocks.Descendants("item")
               let strTitle = xe.Element("title").Value
               let strEpNum = EpNum(xe.Element("guid"))
               let strMp3 = UnUrl(xe.Element("enclosure").Attributes("url").First())
               let strDesc = xe.Element("description").Value
               where strTitle.ToUpper().Contains(strUpSubj)
               || strDesc.ToUpper().Contains(strUpSubj)
               || strEpNum.Equals(strUpSubj)
               select new CDnrResponse(strTitle, strMp3, strEpNum)
            ).Distinct().ToList<CDnrResponse>();
         }
         catch (Exception exc)
         {
            blnRetVal = false;
            strError = exc.Message;
         }

         return blnRetVal;
      }
   }
}


 

posted @ Tuesday, December 29, 2009 11:32 PM
Print

Comments on this entry:

# re: Linq to XML Example (Parsing RSS XML)

Left by John Dauphine at 12/30/2009 10:02 PM
Gravatar
You may find this Powershell example interesting.

Scott Hanselman's Computer Zen - Download Podcasts with Powershell http://bit.ly/6YWwRY

Your comment:



(not displayed)

 
 
 
 

Live Comment Preview: