(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Сам себе RSS-ридер

Источник: habrahabr
Рамиль Алиякберов

Однажды, в середине 5-го курса, попросила меня одногрупница помочь ей с лабами по C#, так как его она только изучала. Узнав задание - "написать RSS ридер" и, оценив ситуацию (конец семестра), я решил ей помочь, так как RSS-ридер нужен был самому.

Немного теории

RSS - это формат передачи веб-контента. Название технологии - акроним "Really Simple Syndication", то есть, "по-настоящему простая передача информации".
RSS - это диалект XML. Все файлы RSS обязаны соответствовать спецификации XML1.0, опубликованной на веб-сайте консорциума WWW (W3C).
На высшем уровне документ RSS представляет собой элемент <rss> с обязательным атрибутом version, указывающим версию RSS(кстати, я свое приложение делал опираясь на RSS 2.0). Дочерний элемент <rss> - один элемент <channel>, который включает информацию о канале (метаданные) и его содержимое.
Пример файла RSS 2.0 выглядит так: 

<?xml version="1.0"?>
<rss version="2.0">
    <channel>
       <title>Liftoff News</title>
       <link>http://liftoff.msfc.nasa.gov/</link>
       <description>Liftoff to Space Exploration.</description>
         <item>
          <title>Star City</title>
          <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
          <description>
             How do Americans get ready to work with Russians aboard the
             International Space Station? They take a crash course in culture, language
             and protocol at Russia`s Star City.
          </description>
          <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
         </item>
         <item>
          <title>Space Exploration</title>
          <link>http://liftoff.msfc.nasa.gov/</link>
          <description>
             Sky watchers in Europe, Asia, and parts of Alaska and Canada
             will experience a partial eclipse of the Sun on Saturday, May 31st.
          </description>
          <pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate>
         </item>
    </channel>
</rss>

Достаточно простой файл. Что же он нам предоставляет? Во-первых, канал (Channel, он же Feed) и различную информацию о нем - заголовок, ссылку на официальный сайт, описание канала и т.д.). Во-вторых, список статей/новостей/записей (item) называйте как хотите, а так же свойства этих записей: заголовок, ссылка, описание, дата публикации. На самом деле свойств, как канала, так и записи может быть гораздо больше. Все они приведены в спецификации rss 2.0. Ее перевод, сделанный Алексеем Бешеновым, можно найти здесь. Последнюю версию спецификации можно найти здесь.
Для работы с информацией предоставляемой rss нам понадобится 3 класса: класс для хранения канала, назовем его RssFeed; класс для хранения списка записей - RssItems; класс для хранения записи - RssItem.

Создание формы

Открываем Microsoft Visual Studio 2005 (линуксойды открывают MonoDevelop) и создаем новое приложение. Назовем его RssReader. Интерфейс бедет простейший.

Первый и основной контрол, который нужно разместить на форме - это TableLayoutPanel, растянутый на всю форму (Dock=Fill). Это очень удобный контрол, он представляет собой таблицу, в каждой ячейке которой можно разместить какой либо элемент интерфейса (элемент может занимать несколько столбцов и/или строк таблицы одновременно). Размеры столбцов могут быть фиксированными либо указываться в процентах от размера таблицы. Это очень удобно при изменении размеров таблицы.
Назначение остальных элементов, думаю, понятно: TextBox - ввод адреса канала; Button - кнопка для обновления фида; в ListView - выводится список записей; в WebBrowser - выводится содержание записи.
Теперь начнем разработку самого ридера. Начнем мы ее с низов, а именно с класса RssItem, затем создадим класс RssItems, последним будет написан RssFeed.

RssItem

Этот класс предоставляет нам информацию о записи. Согласно спецификации rss 2.0 "все элементы <item> являются необязательными, однако, по крайней мере, <title> или <description> должен существовать".
Добавим к проекту новый класс, назовем его RssItem. В итоге получаем следующее:

using System;
using System.Collections.Generic;
using System.Text;

namespace RssReader
{
   class RssItem
   {
   }
}

Наш класс будет хранить минимальную информацию о записи: title (заголовок), link (ссылка на полный текст) и description (краткий обзор сообщения). Добавим к классу 3 публичных поля, для хранения этой информации:

class RssItem
 {
   public String title; // заголовок записи
   public String link; // ссылка на полный текст
   public String description;// описание записи
 }

Теперь добавим конструктор, который будет заполнять эти свойства. На вход конструктору должна передаваться конкретная запись из rss. Так как rss это всего лишь диалект XML, то передаем мы на вход конструктору ветвь <item>. Да. Далее конструктор циклически перебирает каждый тег находящийся внутри полученной записи и, встречая нужный тег, записывать из него информацию в соответствующее свойство класса. Реализовано это может быть так:

///
/// Конструктор для заполнения записи
///
/// xml-тэг для чтения
public RssItem(XmlNode ItemTag)
{
  // просматриваем все теги записи
  foreach (XmlNode xmlTag in ItemTag.ChildNodes)
  {
    // проверяем имя тега, если соответствует одному из укаазных,
    // то в соответствующее свойство объекта записывается содержимое тега
    switch (xmlTag.Name)
    {  
     case "title":
        {
          this.Title = xmlTag.InnerText;
          break;
        }  
     case "description":
        {  
          this.Description = xmlTag.InnerText;
          break;
        }
      case "link":
        {
          this.Link = xmlTag.InnerText;
          break;
        }
    }
  }
}

Да, кстати, так как мы работаем с XmlNode, нужно включить соответствующую сборку в using секцию:

using System.Xml;

RssItems

Этот класс у нас представляет собой список всех записей фида. Его будем реализовывать посредствам генериков, а именно моего любимого генерик класса List. А нравится мне этот генерик тем, что предоставляет очень удобные методы работы с массивами данных.
Добавим к проекту новый класс, и назовем его RssItems. Получаем следующее:

using System;
using System.Collections.Generic;
using System.Text;
namespace RssReader
{
  class RssItems
  {
  }
}

Далее наследуем RssItems от List, вместо T указав тот тип, объекты которого будут храниться в списке. Заодно переопределим метод Contains, для определения существования записи в списке по ее заголовку.

///
/// Проверка существования указаного элемента в списке
///
/// Объект для сравнения
/// true, если объект в списке есть, иначе false
new public bool Contains(RssItem Item)
{
  foreach (RssItem itemForCheck in this)
  {
  // Сравниваем заголовки записей
    if (Item.Title == itemForCheck.Title)
    {
      // нашли совпадение. возвращаем истину
      return true;
    }
  }
  // совпадений не найдено. возвращаем лож
  return false;
}

Так же было бы не плохо иметь возможность поулчить интересующую нас запись из списка используя ее заголовок. Для этого напишем еще один метод, подобный методу Contains:

///
/// Получить запись из списка, по ее заголовку
///
/// Заголовок записи
/// Если запиь существует, то она возвнащается, иначе возвращается null
public RssItem GetItem(String Title)
{   foreach (RssItem itemForCheck in this)
  {
    // Сравниваем заголовок записей с запросом
    if (Item.Title == Title)
    {
      // нашли совпадение. возвращаем найденую запись
      return itemForCheck;
    }
  }
  // совпадений не найдено.
  return null;
}

RssFeed

Вот и добрались до основного класса нашего ридера. Этот класс будет хранить информацию о канале. Согласно спецификации rss 2.0 к обязательным элементам канала относятся: <title> - название канала, по которому люди будут ссылаться на сервис; <link> - URL веб-сайта, связанного с каналом; <description> - фраза или предложение для описания канала.
Снова добавляем новый класс к проекту и называем его RssFeed.

using System;
using System.Collections.Generic;
using System.Text;

namespace RssReader

    class RssFeed
    {
    }
}

Так как все вышеперечисленные свойсвта канала обязательны, то нам необходимо добавить их и в наш класс. Так же мы добавляем свойство типа RssItems, для хранения списка записей канала:

class RssFeed
{
  public String Title; // заголовок канала
  public String Description; // описание канаала
  public String Link; // ссылка на связаный с каналом веб-сайт
  public RssItems Items; // список записей канала
}

Все что осталось сделать теперь, это написать конструктор класса, который будет получать, в качестве параметра, ссылку на rss канал, и, если rss там действительно существует, заполнять свойства создаваемого объекта данными из rss (надеюсь по коду вопросов не возникнет, я постарался его подробно прокомментировать):

///
/// Конструктор для заполнения данных канала
///
/// Адрес канала
public RssFeed(String Url)
{
  // Инициализируем список записей
  Items = new RssItems();
  // Создаем ридер для чтения Rss из указаного адреса
  XmlTextReader xmlTextReader = New XmlTextReader(Url);
  // создаем новый xml документ, для записи в него оплученого RSS
  XmlDocument xmlDoc = New XmlDocument();
  try
  {
    // загружаем RSS в документ с помощью ридера
    xmlDoc.Load(xmlTextReader);
    // закрываем ридер за ненадобностью
    xmlTextReader.Close();
    // так как вся информация об RSS-фиде записана между тегов ,
    // грузим получаем эту ветку.
    XmlNode channelXmlNode = xmlDoc.GetElementsByTagName("channel")[0];
    // если ветка существует, то начинаем заоплнять свойства объекта 
    // данными из ветки
    if (channelXmlNode != null)
    {
      // перебираем всех потомков тега
      foreach (XmlNode channelNode in channelXmlNode.ChildNodes)
      {
        // если имя тега-потомка с интересующим нас, то записываем его данные
        // в определенное совйство объекта
        switch (channelNode.Name)
        {
          case "title":
            {
              Title = channelNode.InnerText;
              break;
            }
          case "description":
            {
              Description = channelNode.InnerText;
              break;
            }
          case "link":
            {
              Link = channelNode.InnerText;
              break;
            }
          case "item": // если имя проверяемого тега равно item, то
            {
              // создаем из этого тега новый объект типа запись
              RssItem channelItem = new RssItem (channelNode);
              // и добавляем его к списку записей канала
              Items.Add(channelItem);
              break;
            }
        }
      }
    }
    else // если в полученом файле тега не найдено, то выбрасываем исключение
    {
      throw New Exception("Ошибка в XML. Описание канала не найдено!");
    }
  }
  // если url канала указане не верно то выбрасываем исключение о недоступности источника   
  catch (System.Net.WebException ex)
  {
    if (ex.Status == System.Net.WebExceptionStatus.NameResolutionFailure)
      throw new Exception("Невозможно соединиться с указаным источником.\r\n" + Url);
    else throw ex;
  }
  // если в качестве адреса RSS был указан локальный пути, который еще и не существует,
  // то выбрасываем соответствующее исключение
  catch (System.IO.FileNotFoundException)
  {
    throw New Exception("Файл " + Url + " не найден!");
  }
  // ну и на последок, ловим все остальные исключения, и передаем их дальше, как есть
  catch (Exception ex)
  {
    throw ex;
  }
  finally
  {
    // закрываем ридер
    xmlTextReader.Close();
  }
}

Финальная стадия

Теперь, все что осталось сделать, это написать код обработчиков событий нажатия кнопки "обновить" и выбора элемента в ListView, а так же добавить глобальную переменную CurrentFeed, в которой будет храниться загруженный канал:

// Глобальная переменная хранящая данные канала
RssFeed CurrentFeed;

// оброботка нажатия на кнопку "Обновить"
private void btRefresh_Click(object sender, EventArgs e)
{
  // Проверяем задан ли адрес
  if (!String.IsNullOrEmpty(tbUrl.Text))
  {
    // Очищаем ListView перед добавлением новых данных
    lvNews.Clear();
    // Инициализируем канал
    CurrentFeed = new RssFeed(tbUrl.Text);
    foreach (RssItem feedItem in CurrentFeed.Items)
    {
      // создаем элемент для вывода в ListView
      ListViewItem listViewItem = new ListViewItem(feedItem.Title);
      // задаем его имя
      listViewItem.Name = feedItem.Title;
      // заносим его в ListView
      lvNews.Items.Add(listViewItem);
    }
  }
}
 
// Оброботка смены выбора элемента в ListView
private void lvNews_SelectedIndexChanged(object sender, EventArgs e)
{
  // получаем связаную с выбраным ListViewItem новость
  if (lvNews.SelectedItems.Count > 0 && // проверяем что чтото действительно выбрано
      CurrentFeed != null && // проверяем, что канал инициализирован
      CurrentFeed.Items.Count > 0 // проверяем существование записей в канале
      )
  {
    // выводим полный текст выбраной записи
    wbDescription.DocumentText = CurrentFeed.Items.GetItem(lvNews.SelectedItems[0].Text).Description;
  }
}

Заключение

В итоге мы получили простой RSS-ридер, который может читать фиды стандарта 2.0. В следующей статье я постараюсь рассказать, как можно сделать наши классы более универсальными, а так же как можно организовать хранение истории посещенных лент.

PS: конструктивная критика, а так же предложения и пожелания приветствуются.

 

Ссылки по теме

Файлы для загрузки


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 18.06.2008 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Raize Components 6
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
Microsoft Windows Professional 10, Электронный ключ
JIRA Software Commercial (Cloud) Standard 10 Users
IBM RATIONAL Rose Enterprise Floating User License + Sw Subscription & Support 12 Months
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
СУБД Oracle "с нуля"
Новые материалы
Один день системного администратора
Программирование на Visual Basic/Visual Studio и ASP/ASP.NET
Мастерская программиста
Adobe Photoshop: алхимия дизайна
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100