XML
RSS
Exceptions
Børre Stenseth
Moduler>XML>RSS-leser

RSS-Leser

Hva
Image1
En svært enkel RSS-leser

RSS er et akronym for Really Simple Syndication [1] , [2] . Andre variaenter brukes: Rich Site Summary, RDF Site Summary. Hvis du lurer på hva "syndication" står for så kan følgende forklaring av substantivet "syndicate" kanskje hjelpe ( fra www.wordsmyth.net):

an agency that purchases articles, photographs, and the like for distribution to a number of different newspapers and periodicals.

RSS er en standard for å publisere informasjon via linker til vevsider. En publisist kan velikeholde en RSS-fil og lesere kan abonnere på denne fila og lese og tolke den via en RSS-leser. Dette eksempelet er en slik minimalistisk RSS-leser.

RSS kommer i flere versjoner og er et XML-basert format, [3] . Se referanser for mer om RSS. Et eksempel på en fungerende RSS-fil kan du se her: sample_rss.xml.

Kort så består fila av et channel - element som beskriver pubisisten og som videre består av et antall items. Hvert item har blandt annet en tittel, en beskrivelse og en link. Det er disse tre egenskapene ved et item vi vil benytte oss av.

Programmet vårt skal abonnere på et antall RSS-filer. Vi lager en klasse "feedlist" for å administrere en slik liste med adresser til RSS-filer.

Hvert feed er beskrevet med en instans av klassen "feed". Den samme klassen brukes også til å ta vare på et Item (en nyhetsrefreanse).

feed

class feed : Object
{
    protected String m_title;
    protected String m_description;
    protected Uri m_link;
    protected XmlDocument m_xmldoc;
    // constructor based on strings
    public feed(string t, string u, string d)
    {
        title = t;
        link = new Uri(u);
        description = d;
    }
    // constructor based on feed-node from XML
    public feed(XmlNode nod)
    {
        this.title = String.Empty;
        this.link = null;
        this.description = String.Empty;
        if (nod.HasChildNodes)
            foreach (XmlNode p in nod.ChildNodes)
                if (p.GetType() == typeof(XmlElement))
                {
                    if (p.LocalName.CompareTo("title") == 0)
                        this.title = p.InnerText;
                    if (p.LocalName.CompareTo("link") == 0)
                        this.link = new Uri(p.InnerText);
                    if (p.LocalName.CompareTo("description") == 0)
                        this.description = p.InnerText;
                }
    }
    public string title
    {
        get { return m_title; }
        set { m_title = value; }
    }
    public Uri link
    {
        get { return m_link; }
        set { m_link = value; }
    }
    public string description {
        get { return m_description; }
        set { m_description = value; } 
    }
    public XmlDocument xmldoc{
        set { m_xmldoc = value; }
        get {             
            // done already ?
            if (m_xmldoc != null)
                return m_xmldoc;
            // get it 
            try
            {
                m_xmldoc = new XmlDocument();
                m_xmldoc.Load(link.ToString());
                return m_xmldoc;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }
                
    // will need this as feeds appear in listboxes
    public override string ToString(){return title;}
    // prepare a feed-node as string
    public String GetPacked() 
    {
        return String.Format(@"
            <title>{0}</title>
            <link>{1}</link>
            <description>{2}</description>", 
            title, link.ToString(), description);
    }
}

feedlist

class feedlist:Object
{
    // keep up a number of feeds, which sleeps on a file
    private ArrayList FeedList;
    private string filename;
    
    public feedlist(String filename)
    {
        FeedList = new ArrayList();
        this.filename = filename;
        LoadFeeds();
        // something to get started with if we have not loaded anything:
        if (FeedList.Count == 0)
            FeedList.Add(new feed(
                "Wired News",
                "http://www.wired.com/news_drop/netcenter/netcenter.rdf",
                "Tech review"));
    }
    public ArrayList getFeedList(){    return FeedList;}
    public void LoadFeeds()
    {
        // load as XML
        XmlDocument doc = new XmlDocument();
        try
        {
            doc.Load(filename);
            XmlNodeList list = doc.GetElementsByTagName("feed");
            foreach(XmlNode n in list)
                FeedList.Add(new feed(n)); 
        }
        catch (Exception ex)
        {
            MessageBox.Show(
                            "Kan ikke laste opp kildefila: "+ex.Message,
                            "Feilmelding",
                            MessageBoxButtons.OK);
            return;
        }
    }
    public void SaveFeeds()
    {
        // save as text in XML-format
        FileStream s = null;
        StreamWriter w = null;
        try
        {
            s = new FileStream(filename, FileMode.Truncate);
            s.Position = 0;
            w = new StreamWriter(s);
            w.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            w.WriteLine("<feeds>");
            for (int i = 0; i < FeedList.Count; i++)
                w.WriteLine("<feed>"+
                             ((feed)FeedList[i]).GetPacked()+
                            "</feed>");
            w.WriteLine("</feeds>");
            w.Flush();
        }
        catch (Exception e)
        {
            MessageBox.Show(
                            "Kan ikke spare kildefila: "+e.Message,
                            "feilmelding",
                            MessageBoxButtons.OK);
            return;
        }
        finally
        {
            if (w != null)    
                w.Close();
        }
    }
}

FeedForm

Når vi skal endre et feed eller lage et nytt feed, gjør vi dette via en dialogboks: "FeedForm".

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace RSS
{
    public partial class FeedForm : Form
    {
        public Uri link;
        public String title;
        public FeedForm(String t, String l)
        {
            InitializeComponent();
            this.textBoxTitle.Text = t;
            this.textBoxLink.Text = l;
        }
        private void buttonOK_Click(object sender, EventArgs e)
        {
            // is data ok
            try
            {
                link = new Uri(this.textBoxLink.Text);
                if ((this.textBoxTitle.Text==null)||
                    (this.textBoxTitle.Text.Length == 0))
                    title = this.textBoxLink.Text;
                else
                    title = this.textBoxTitle.Text;
                this.DialogResult=DialogResult.OK;
                this.Hide();
            }
            catch(Exception ex){
                DialogResult result;
                result = MessageBox.Show(this,
                                "Kildens adresse (URL) ser ikke bra ut, korrigere ?",
                                "D�rlig lenke",
                                MessageBoxButtons.YesNo);
                if (result == DialogResult.Yes)
                    return;
                this.DialogResult = DialogResult.Cancel;
                this.Hide();
            }
        }
    }
}

Selve logikken er plassert på "Form" klassen:

Form

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;
namespace RSS
{
    public partial class Form1 : Form
    {
        // will be based on a list of feeds
        feedlist theFeeds;
        
        public Form1()
        {
            InitializeComponent();
            // establish list of feeds and fill left listbox, feedlist
            theFeeds = 
                new feedlist(Path.Combine(Application.StartupPath, "..\\..\\feeds.xml"));
            if (theFeeds == null)
                return;
            for(int i=0;i<theFeeds.getFeedList().Count;i++)
                listBoxFeeds.Items.Add(theFeeds.getFeedList()[i]);
            
            // set up a welcome message in webbrowser
            webBrowser1.DocumentText = Properties.Resources.startpage; 
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // store list of feeds
            theFeeds.SaveFeeds();
        }
        private void FillUpItemlist(feed f)
        {
            // fill up listBoxItems with items from a selected feed
            XmlDocument doc = f.xmldoc;
            if (doc == null)
            {
                DialogResult result;
                result = MessageBox.Show(this, 
                                "Denne kilden er ikke tilgjengelig",
                                "Finner ikke kilden",
                                MessageBoxButtons.OK);
                return;
            }
            // extract the items and fill up: listBoxItems
            XmlNodeList list = doc.GetElementsByTagName("item");
            listBoxItems.Items.Clear();
            for (int nix = 0; nix < list.Count; nix++)
            {
                String strTitle = null;
                String strLink = null;
                String strDesc = null;
                XmlNode p = list.Item(nix);
                if (p.HasChildNodes)
                {
                    foreach (XmlNode c in p.ChildNodes)
                    {
                        if (c.GetType() == typeof(XmlElement))
                        {
                            if (c.LocalName.CompareTo("title") == 0)
                                strTitle = c.InnerText;
                            if (c.LocalName.CompareTo("link") == 0)
                                strLink = c.InnerText;
                            if (c.LocalName.CompareTo("description") == 0)
                                strDesc = c.InnerText;
                        }
                    }
                }
                listBoxItems.Items.Add(new feed(strTitle, strLink, strDesc));
            }
        }
        // get all items from this feed
        private void listBoxFeeds_MouseClick(object sender, MouseEventArgs e)
        {
            // we have clicked on a listitem, which one ?
            int ix = listBoxFeeds.IndexFromPoint(e.X, e.Y);
            if (ix != -1)
            {
                // get all items in the actual feed
                feed f = (feed)listBoxFeeds.Items[ix];
                FillUpItemlist(f);
            }
            String tmp = listBoxFeeds.SelectedItem.ToString();
            webBrowser1.DocumentText = "<html><body><h2>" + tmp + "</h2></body></html>";
        }
        // produce description of selected item in webbrowser
        private void listBoxItems_MouseClick(object sender, MouseEventArgs e)
        {
            // we have clicked on a listitem, which one ?
            int ix = listBoxItems.IndexFromPoint(e.X, e.Y);
            if (ix != -1)
            {
                // get information from the actual feeditem
                feed f = (feed)listBoxItems.Items[ix];
                String description = f.description;
                String title = f.title;
                Uri link = f.link;
                // produce HTML
                String htmlS= 
                    String.Format(Properties.Resources.skeleton,title,description,link);
                webBrowser1.DocumentText = htmlS;
            }
        }
        // we have a right-click in itemlist
        private void contextMenuStrip1_ItemClicked(object sender, 
                                ToolStripItemClickedEventArgs e)
        {
            //Which feed is selected ?
            feed theFeed=(feed)listBoxFeeds.SelectedItem;
            
            // what do we want to do with it ?
            if (e.ClickedItem == this.renameToolStripMenuItem)
            {
                // change
                FeedForm ff = 
                    new FeedForm(theFeed.title,theFeed.link.ToString());
                ff.ShowDialog(this);
                if (ff.DialogResult == DialogResult.OK)
                {
                    theFeeds.getFeedList().Remove(theFeed);
                    theFeeds.getFeedList().Add(
                        new feed(ff.title,ff.link.ToString(),""));
                    listBoxFeeds.Items.Clear();
                    for (int i = 0; i < theFeeds.getFeedList().Count; i++)
                    {
                        feed afeed=(feed)theFeeds.getFeedList()[i];
                        listBoxFeeds.Items.Add(afeed);
                    }
                }
            }
            else if (e.ClickedItem == this.deleteToolStripMenuItem)
            {
                // delete it, after a warning
                DialogResult result;
                result = MessageBox.Show(this, 
                                "Vil du virkelig fjerne denne kilden ?",
                                "Fjern kilde",
                                MessageBoxButtons.YesNo);
                if (result == DialogResult.Yes)
                {
                    theFeeds.getFeedList().Remove(theFeed);
                    listBoxFeeds.Items.Clear();
                    for (int i = 0; i < theFeeds.getFeedList().Count; i++)
                        listBoxFeeds.Items.Add(theFeeds.getFeedList()[i]);
                    listBoxItems.Items.Clear();
                }
            }
            else if (e.ClickedItem == this.updateToolStripMenuItem)
            {
                // update the feed theFeed
                theFeed.xmldoc = null;
                FillUpItemlist(theFeed);
            }
        }

        // add a feed
        private void buttonNew_MouseClick(object sender, MouseEventArgs e)
        {
            //Introduce a new feed
            FeedForm ff = new FeedForm("<title>", "<uri>");
            ff.ShowDialog(this);
            if (ff.DialogResult == DialogResult.OK)
            {
                theFeeds.getFeedList().Add(
                    new feed(ff.title, ff.link.ToString(), ""));
                listBoxFeeds.Items.Clear();
                for (int i = 0; i < theFeeds.getFeedList().Count; i++)
                {
                    feed afeed = (feed)theFeeds.getFeedList()[i];
                    listBoxFeeds.Items.Add(afeed);
                }
            }
        }
        // update all feeds    
        private void buttonUpdate_MouseClick(object sender, MouseEventArgs e)
        {
            if (theFeeds.getFeedList().Count > 0)
            {
                for (int i = 0; i < theFeeds.getFeedList().Count; i++)
                {
                    feed afeed = (feed)theFeeds.getFeedList()[i];
                    afeed.xmldoc=null;
                }
                // select and show first
                listBoxFeeds.SelectedIndex=0;
                FillUpItemlist((feed)listBoxFeeds.SelectedItem);
            }
        }
        // see you later...
        private void buttonExit_MouseClick(object sender, MouseEventArgs e)
        {
            this.Close();
            this.Dispose();
        } 
    }
}

Som du ser dersom du kjører programmet så er det en rekke ting som kan forbedres. Dersom programmet skulle utvikles i retning av et produkt så måtte vi i hvert fall vurdere å:

  • forbedre sikkerheten
  • bruke threads for oppdatering i bakgrunnen
  • forbedre brukergrensesnittet funksjonelt
  • gjennomføre en kraftig estetisk "makeover"
  • trekke ut mer informasjon fra RSS-filene for å få fram bedre beskrivelse av kildene.
  • la programmet ha hukommelse slik at det i tillegg til å huske de kildene vi abonnerer på, også husker et antall tidligere (permanent) items fra disse kildene.
Referanser
  1. RSSWikipedia2009Wikipediaen.wikipedia.org/wiki/RSS_(protocol)14-03-2010
  1. RSSPilgrim. Mark2002O'Reillywww.xml.com/pub/a/2002/12/18/dive-into-xml.html14-03-2010
  1. RSS 2.0 at Harvard Lawwiner, Dave2003Harvard Lawcyber.law.harvard.edu/rss/rss.html14-03-2010
  • Prosjekt:
    https://svn.hiof.no/svn/psource/Csharpspikes/rss
Vedlikehold

B.Stenseth, september 2005

(Velkommen) Moduler>XML>RSS-leser (LINQ)