[X] Lesen von RSS-Feed durch Einsatz des DataSet



  • RSS-Feed mit dem DataSet

    Autor: sclearscreen

    IDE: Visual Studio 2003 Standard

    Betriebssystem: WindowsXP

    Vorwort:

    Ganz kurz gesagt: Wer sich meinen vorherigen Beitrag angeschaut und das Projekt gezogen hat, wird beim Testen einen Bug feststellen.

    Es ist kein schwerwiegender Fehler, der den ganzen Desktop-PC zum Abstürzen bringt. Dieser Bug
    veranlasst auch nicht das Programm, sich ins System zu verabschieden. Nein, das Programm parst beim Umgang mit RSS-Feed, einer bestimmten Version, nicht alle Daten aus (sprich das Parsen des XML funktioniert nicht richtig). Bei dem Algorithmus zum Parsen des XML hatte ich voll auf Eigenbau gesetzt, was nicht ganz mit Erfolg gekrönt war. Soll ich als Autor dabei nun lachen oder weinen? Eher lachen - es zeigt einem, es geht besser, und zudem hat man gleich
    Stoff für einen neuen Beitrag.

    Also auf ans Werk.

    Inhalt:

    1 Refactoring des Programms
    1.1 Wo muss das Refactoring angesetzt werden?
    1.2 Was hat das Refactoring am Code verändert?
    2 Was lernt man daraus?
    3 Zum Testen gebe ich dem, der möchte, die Lösung noch mal auf den Weg
    3.1 Die Methode ohne Verwendung eines Proxyservers
    3.2 Die Methode mit Verwendung eines Proxyservers
    3.3 Verwenden kann man die zwei Methoden dann beispielsweise so
    4 Haftungsauschluss für etwaige Schäden an Hardware/Software des Users

    1.Refactoring des Programms
    1. 1 Wo muss das Refactoring angesetzt werden?

    Ich werde vorher mit Hilfe von UML den Weg der Daten bis zum Bug deutlich machen und die fehlerhafte Implementierung dieser Stelle dann aufzeigen. Sollte manches an der UML-Symbolik nicht ganz so stimmen, bitte ich das zu verschmerzen. Ich benutze OpenOffice was nicht alle Standardformen wie Rechtecke und Verbindungspfeile bereitstellt, um manche UML-Symbolik korrekt ausdrücken zu können. Daher ist es mir nicht möglich, spezielle Besonderheiten bei UML zu berücksichtigen. Beispielsweise kann man durch Verbindungspfeile
    kein asynchrones Verhalten bei Prozessaufrufen darstellen. Wo man einen Verbindungspfeil mit einer halben Pfeilspitze zur Darstellung bräuchte. So, genug abgeschweift, zur Sache.

    Wir beginnen mit einem Aktivitätendiagramm, was die Interaktion des Users mit seinem Reader kurz nach dem Start der Anwendung zeigt.

    Ich hoffe, jetzt fällt dem aufmerksamen Leser etwas auf, was uns die UML-Symbolik als Signal verkauft/darstellt! Dies ist eine Methode, die als Callback-Funktion für den Button genutzt wird.
    Und eben diese Callback-Funktion, stößt ihrerseits in ihrem Rumpf den Code zum Download und Parsen des Feeds an. Genau dort liegt der Hase im Pfeffer.
    In diesem Code interagieren 4 Klassenobjekte miteinander, um das XML-Script des RSS-Feeds per Http zu holen. Eines der Klassenobjekte ist eine Instanz des Typs XmlTextReader.
    Der Algorithmus des Parsens bestand nun darin, genau dieses Objekt mit Ablaufstrukturen sinnvoll zu kombinieren, um an die heiß begehrten item-Tags zu kommen.

    Genau dies passiert in folgendem Block:

    Die Implementierung zu diesem Block findet man in/unter folgender Methode (siehe Projekt aus meinem ersten Beitrag)

    private void button1_Click(object sender, System.EventArgs e)
    {
    	.
    	.
    	.
    }
    

    Innerhalb der Methode wird per Http über 2 Klassen

    WebRequest und WebResponse ein Stream-Objekt gebildet, was einem XmlTextReader gegeben wird.

    Der ganze Weg bis zur Stream-Gewinnung und der Instanzierung des XmlTextReader
    arbeitet normal und nach meiner Erwartung.

    XmlTextReader xmltextReader = new XmlTextReader(this.rssFeed.GetResponse().GetResponseStream());
    

    Nach dieser Zuweisung beginnt der Eigenbaualgorithmus zum Parsen des XML, der sich als zu unflexibel gegenüber dem RSS-Feed der Version 1.0 erweist.

    Zwei der der drei notwendigen Tags werden nicht ausgeparst. Es entsteht dabei auch keine Endlosschleife, die das GUI einfrieren lässt.

    Kurzerhand hatte ich mir einen Feed der Version 1.0 auf meine Platte geladen. Da die Klasse XmlTextReader auch aus Dateien lesen kann, habe ich die Instanzierung auf diese XML-Datei umgestellt. Somit konnte ich problemloser schrittweise debuggen, ohne auf das Netzwerk/Internet angewiesen zu sein.
    Dies hätte unter Umständen das Debuggen erschwert.

    Nach der Umstellung auf den nun lokalen Feed der Version 1.0 stellt man fest, dass der Algorithmus dann eben bei jedem „item-Tag“ nur einen der drei Tags ausparst. Hat der Algorithmus dies gemacht, springt dieser zum nächsten „item-Tag“ und vergisst somit einen Child-Knoten zu lesen. Dies genau ist besagter Bug.

    Ich gebe zu, an dem Punkt hatte mir dann die Muse gefehlt, um den Algorithmus zu verbessern. Die Gefahr einer Endlosschleife innerhalb der while-Schleife erachte ich zu groß.

    Eine einfacherer Lösung auf Basis von vorhandenen Komponenten ist immer besser, als das Rad weiter neu zu erfinden.

    Da ich wusste, dass die Klasse DataSet mit XML umgehen kann - gerade im Bezug auf Datenbanken -, versuchte ich 1 und 1 zusammenzuzählen.

    RSS-Feeds sind aufgrund ihrer internen Struktur datenzentriert, also ideal zur Arbeit mit Datenbanken, ausgelegt. Hinzu kommt, dass das DataSet XML sowohl laden als auch speichern kann; beim Laden kann es seine Daten aus einem Stream-Objekt bekommen und auch von einem XmlTextReader-Objekt.

    Also wenn sich diese Gedanken nicht gut anhören! Es ergab sich eine funktionsfähige Lösung innerhalb von 10-15 Minuten. Das Hilfesystem und die MSDN haben sich dabei wieder einmal bewährt.

    1.2 Was hat das Refactoring am Code verändert?

    Folgender Code wurde somit aus dem Projekt durch Auskommentieren verbannt:

    while(xmltextReader.Read() == true) 
    {
    	if(xmltextReader.Name == "item") 
    	{
    		while(xmltextReader.Read()) // weiterlesen im Element <ITEM>
    		{
    			switch(xmltextReader.Name) // lesen bis wir ....
    			{
    				case "title": 
    				{
    					if(titlereaded != true) 
    					{
    						while(xmltextReader.Read() == true && xmltextReader.NodeType != XmlNodeType.Text);
                            this.textBox2.Text = this.textBox2.Text + "TITLE: " + xmltextReader.Value + "\r\n"; 
    				        titlereaded = true; // merken uns, dass Titel gefunden ist
    					}
    					break;
    				}
    
    				case "link":
    				{
    					if(linkreaded != true) // doppeltes Lesen verhindern
    					{
    						while(xmltextReader.Read() == true && xmltextReader.NodeType != XmlNodeType.Text);
    				        this.textBox2.Text = this.textBox2.Text + "LINK: " + xmltextReader.Value + "\r\n"; 
    				        linkreaded = true; // merken uns, dass Link gefunden ist
    					}
    					break;
    				}
    
                                         // und auch das 3. Kernelement Description wurde gefunden 
    				case "description":
     		       {
                                                  // doppeltes Lesen der Description verhindern
    					if(descriptionreaded != true) 					
                        {
    						while(xmltextReader.Read() == true && xmltextReader.NodeType != XmlNodeType.Text);
    				        this.textBox2.Text = this.textBox2.Text + "DESCRIPTION: " + xmltextReader.Value + "\r\n"; 
    						descriptionreaded = true; // merken uns, dass Description ist
    					}
    					break;
    				}
    
    				default: break; // hier sind es optionale Tags des <ITEM> - Elements
    			}
    
    			// Wir prüfen jetzt, ob wir alle 3 Kernelemente des <ITEM> - Elements gefunden haben
    			if(titlereaded == true && linkreaded == true && descriptionreaded == true) //
    								break; // alles nötige für diesen einen News-Feed ist ausgelesen 		}
    		titlereaded = false;
    		linkreaded = false;
    		descriptionreaded = false;
    		this.textBox2.Text = this.textBox2.Text + "---------------------------------\r\n"; 
    	}
    }
    

    Das vorangegangene Listing wird ersetzt durch folgende kurze Sequenz. Ich war jedenfalls froh, dass dieses Geschoss vom Tisch war.

    DataSet ds                  = new DataSet("RSS-Feed: " + this.textBox1.Text);
    System.IO.Stream stream     = this.rssFeed.GetResponse().GetResponseStream();
    XmlTextReader xmltextReader = new XmlTextReader(stream);
    ds.ReadXml(xmltextReader);
    DataTable dt = ds.Tables["item"];
    

    Der neue Code liest sich auch viel besser. Und man bekommt auch einen Wink mit dem Zaunpfahl, was Wiederverwendung von getesteten Komponenten ausmacht.

    Und das bekommt man so ganz gratis mit dem Framework dazu. Eine Lösung mit 5 Zeilen Code gegenüber ich weiß nicht wie vielen Zeilen. Diese 5 Zeilen ersetzen die While-Schleife mit den ganzen eingeschachtelten Ablaufstrukturen.

    Man braucht sich jetzt wirklich nur ein DataGrid auf sein Formular ziehen, weist dessen Eigenschaft DataSource einer der Variablen ds oder dt zu und schon funktioniert beides.

    Im Verlauf der nächsten Sachen ,die man mit dem DataGrid anstellen kann, erinnert man sich
    an die Sache mit dem HitTestInfo. Da bekommt man angeklickte bzw. aktivierte Zellen im DataGrid
    zurück. Somit kommt man an den URL, der in der Spalte mit dem Namen „link“ im DataGrid auftauchen wird.

    Nachdem man durch folgenden Code...

    datagrid1.DataSource = dt;
    

    ... dem DataGrid das DataSet bekannt macht, ist man in der Lage, über HitTestInfo Informationen aus dem DataGrid zu holen.
    Man kann dem Browser also Informationen aus der Spalte mit
    dem Namen "Link" zuführen.

    Der Browser bietet ja durch das Browser-Control Interoperabilität mit Anwendungen. Das ist somit die ganze Kunst, um einen richtigen Feed-Reader zu bauen.

    2. Was lernt man daraus?

    a) Es ist immer gut und praktikabel, sich mit der Klassenbibliothek des Framework auseinander zu setzen. Es bringt bessere Lösungen.

    b) Man baut ausgiebig auf bereits getesten Code auf, was Wiederverwendbarkeit und Wartbarkeit
    fördert.

    c) Durch Verwendung bestehender getesteter Klassen wird Code sicherer, unter Umständen kürzer
    und auch lesbarer

    d) Die genannten Dinge bringen nur was, wenn man Kenntnisse über das jeweilige Framework hat.

    Man ist im Bezug auf die 4 Punkte immer gut beraten, die MSDN, Foren und das Internet zu benutzen.

    3. Zum Testen gebe ich dem, der möchte, die Lösung noch mal auf den Weg

    folgende Methode kann man in sein Projekt kopieren, einfach in die gewünschte Klasse als Methode reinkopieren. Existiert nicht ohne Grund in 2 Varianten!!!

    3.1 Die Methode ohne Verwendung eines Proxyserver

    // Achtung! Methode kann je nach Netzwerk, wenn ein Proxy zwischen euch sitzt, eine Exception werfen
    private DataTable GetRssFeedItems(string Url)
    {		
    	// Dieser Teil transferiert die Daten des RSS-Feeds zu unserem Rechner. Auch hier 
    	// könnte eine Exception auftreten, aber die Meldung bekommen wir auch in der Statuszeile
    	System.Net.HttpWebRequest rssFeed = (System.Net.HttpWebRequest)
                                                                             (WebRequest.Create(Url));
    
    	 // Der XmlTextReader bekommt einen Netzwerkstream direkt vom HttpWebRequest - Objekt
    	// Dieser Teil könnte eine Exception werfen. Das sehen wir dann aber in der Statuszeile		
    	DataSet ds = new DataSet("RSS-Feed: " + Url);
    	System.IO.Stream stream = rssFeed.GetResponse().GetResponseStream();
    	System.Xml.XmlTextReader xmltextReader = new System.Xml.XmlTextReader(stream);
    	ds.ReadXml(xmltextReader);
    	return (ds.Tables["item"] as DataTable);
          }
    

    3.2 Die Methode mit Verwendung eines Proxyserver

    private DataTable GetRssFeedItems(string Url, 
                                      string proxyAddresse,
                                      string proxyPort,
                                      string username,
                                      string passwort)
    {		
    	// Dieser Teil transferiert die Daten des RSS-Feeds zu unserem Rechner. Auch hier 
    	// könnte eine Exception auftreten, aber die Meldung bekommen wir auch in der Statuszeile
    	System.Net.HttpWebRequest rssFeed = (System.Net.HttpWebRequest)
                                                                             (WebRequest.Create(Url));
    
    	// ToDo: Wenn es hier eine Exception gibt, kann es daran liegen, dass ein Proxy zwischen uns
    	//       und dem Internet sitzt! Wir probieren es daher mit der Proxyauthentifizierung.
    	//       Man nehme diese Proxydaten aus dem lokalen Browser
    	rssFeed.Proxy = new System.Net.WebProxy(proxyAddresse + ":" + proxyPort);
    	rssFeed.Proxy.Credentials = new System.Net.NetworkCredential(username,passwort);
    
    	 // Der XmlTextReader bekommt einen Netzwerkstream direkt vom HttpWebRequest - Objekt
    	// Dieser Teil könnte eine Exception werfen. Das sehen wir dann aber in der Statuszeile		
    	DataSet ds = new DataSet("RSS-Feed: " + Url);
    	System.IO.Stream stream = rssFeed.GetResponse().GetResponseStream();
    	System.Xml.XmlTextReader xmltextReader = new System.Xml.XmlTextReader(stream);
    	ds.ReadXml(xmltextReader);
    	return (ds.Tables["item"] as DataTable);
          }
    

    3.3 Verwenden kann man die 2 Methoden dann beispielsweise so

    try // Versuch, den Stream normal zu holen
    	{
             		this.dataGrid1.DataSource = GetRssFeedItems("http://www.rss-verzeichnis.de/updates.xml");
    	}
    	catch(System.Exception ex) // Exception kommt, wenn ein Proxyserver dazwischen ist 
    	{
    		MessageBox.Show(ex.Message);
    
    		try // Versuch, den Stream zu holen trotz Proxy
    		{ 
    			this.dataGrid1.DataSource = GetRssFeedItems("http://www.rss-verzeichnis.de/updates.xml",
    						                    "100.100.100.100", // P.S.: fiktive ProxyIp
    						                    "8080",            
    							      "AlfonzDerUser",
    						                    "AfonzesPasswort"); 
    		}
    		catch(System.Exception ex)
    		{
    			// Punkt, an dem es keinen Sinn hat, die Anwendung weiter fortzusetzen
    			// Stream konnte mit normaler Internetverbindung nicht geholt werden
    			// Stream konnte auch nicht mit dem zwischengeschalteten Proxy beschaffen werden
    
    			MessageBox.Show(ex.Message);
    
    			// Gründe: 
    			//                 - falscher Proxy (Proxydaten, Proxyserver inaktiv)
    			//                 - Webserver mit RSS Feed inaktiv
    			//                 - Internetverbindung aus verschiedene Gruenden inaktiv 
    		}
    	}
    

    Wie man sieht, ist es ratsam, try/catch zu verwenden, wenn man die Methoden benutzt. Ich habs ja als Kommentar in der einen Metode angekündigt.

    4 Haftungsauschluss für etwaige Schäden an Hardware/Software des Users

    Man kann ja nie wissen! Ich will also hier am Ende noch mal darauf hinweisen, dass jeder, der Inhalte aus meinen Beiträgen nutzt, dies auf eigene Gefahr tut!
    Ich übernehme keinerlei Haftung für Schäden, die durch den Download, der Nutzung oder der Änderung an dem aufgeführten Quellcode aufgetreten sind.

    Ich teile gerne Erfahrungen, aber man muss sich absichern - gerade heute. So, das wäre auch geklärt, denke ich. Bleibt nur noch das Nachwort oder der Nachtrag.

    Nachtrag

    So, damit können Interessierte ihrem Spieltrieb freien Lauf lassen. Die Sache mit dem Mozillabrowser habe ich bewusst außen vor gelassen. Einen Browser noch reinzupacken, sollte kein Problem darstellen.
    Beispiele gibt es zuhauf im Netz. Außerdem: nur durch Rumspielen und Rumexperimentieren erlangt man Wissen.

    Beim DataGrid sollte man an die Sache mit dem HitTestInfo denken! Darüber gelangt man dann an den URL, der im Feed steckt, und kann somit ein Browser-Control mit URLs versorgen.

    Hier braucht man den URL dann nur noch mit der Navigate-Methode des Browserobjekts in Einklang zu bringen und schon hat man den RSS-Reader fertig. Daran lässt sich noch viel machen (Verwaltung von URLs auf die Feeds etc.).

    Den Beitrag als PDF

    http://www.c-plusplus.net/magazin/bilder/RSS/refactoring_RSS_Feed_mit_dem_DataSet.pdf



  • ui ich habe mein "Improved-StripTags" benutzt ich bin begeistert 😃 🙂 😮 👍



  • sclearscreen schrieb:

    ui ich habe mein "Improved-StripTags" benutzt ich bin begeistert 😃 🙂 😮 👍

    Wegen dem "Aktivitätendiagramm" schau dir mal DIA für Windows an 😉

    BR



  • evilissimo schrieb:

    sclearscreen schrieb:

    ui ich habe mein "Improved-StripTags" benutzt ich bin begeistert 😃 🙂 😮 👍

    Wegen dem "Aktivitätendiagramm" schau dir mal DIA für Windows an 😉

    BR

    hmm keine Ahnung was das jetzt mit dem Wrapper zu tun hat der einen
    Prozess von "StripTags" benutzt um das

    STRG+A
    STRG+C
    STRG+V Gedöns per Sendkeys auch noch automatisiert

    als höchstens das ich meine Aktivität beim Umgang mit StripTags wirklich auf das absolut mindeste beschränke "Ein Mausklick" 👍

    Gruss sclearscreen

    Okay ich kenne DIA nicht was ist das? Vielleicht habe ich ja ein zusätzliche
    wieder das gleiche Rad neu erfunden!



  • evilissimo schrieb:

    sclearscreen schrieb:

    ui ich habe mein "Improved-StripTags" benutzt ich bin begeistert 😃 🙂 😮 👍

    Wegen dem "Aktivitätendiagramm" schau dir mal DIA für Windows an 😉

    BR

    Okay geggooglet Diagramme fuer Windows DIA!

    @evilissimo hast dich bestimmt beim zittieren nur verklickt.
    So das Ding wird gleich gezogen...



  • sclearscreen schrieb:

    ui ich habe mein "Improved-StripTags" benutzt ich bin begeistert 😃 🙂 😮 👍

    Ja, ich habs immer noch nicht geschafft, sorry. 😞



  • ähm, von dem korrigierten satz seh ich aber nichts... 😉

    Mr. B



  • Mr. B schrieb:

    ähm, von dem korrigierten satz seh ich aber nichts... 😉

    Mr. B

    Satz VORHER im Abschnitt 1.1:
    Ich benutzte OpenOffice was nicht alle Standardformen Rechtecke, Verbindungspfeile bereitstellt.

    Satz JETZT im Abschnitt 1.1 (Seite 6 ganz unten, gestern Beitrag neu dort abgelegt):
    Ich benutze OpenOffice was nicht alle Standardformen Rechtecke, Verbindungspfeile bereitstellt, um manche UML-Symbolik korrekt ausdrücken zu können.



  • sclearscreen schrieb:

    Ich benutze OpenOffice was nicht alle Standardformen Rechtecke, Verbindungspfeile bereitstellt, um manche UML-Symbolik korrekt ausdrücken zu können.

    das ist der problembereich und an dem hat sich nichts geändert.

    ich benutze OO, (KOMMA!) was nicht alle

    - standardkonformen Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen wie Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen, Rechtecke und Verbindungspfeile bereitstellt

    Oder, falls ich den Sinn immer noch nicht erkannt habe,
    deine Variante.

    Mr. B



  • estartu schrieb:

    sclearscreen schrieb:

    ui ich habe mein "Improved-StripTags" benutzt ich bin begeistert 😃 🙂 😮 👍

    Ja, ich habs immer noch nicht geschafft, sorry. 😞

    @estartu
    Macht ja nix ich hatte ja gesagt ich habe solange eine eigene Implementierung
    gemacht und zusätzlich besagte Features reingepackt.
    Das Ding legt mir sogar in seinem Verzeichnis ein Backup an bevor es losrödelt.

    StripTags_25_6_2006_18_14_40.bak

    Nur falls es mal Misverständnisse bei der Nachredaktion geben sollte
    dann kann man die Nachredaktion zurückverfolgen 👍



  • Mr. B schrieb:

    sclearscreen schrieb:

    Ich benutze OpenOffice was nicht alle Standardformen Rechtecke, Verbindungspfeile bereitstellt, um manche UML-Symbolik korrekt ausdrücken zu können.

    das ist der problembereich und an dem hat sich nichts geändert.

    ich benutze OO, (KOMMA!) was nicht alle

    - standardkonformen Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen wie Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen, Rechtecke und Verbindungspfeile bereitstellt

    Oder, falls ich den Sinn immer noch nicht erkannt habe,
    deine Variante.

    Mr. B

    hmmm ich habs nicht so mit Grammatik 🤡
    kannst ja denn Satz umbauen bitte, auf jedenfall ist jetzt nicht nach dem Wort
    bereitgestellt Schluß. Denn da hattest Du ja vorher den Anmerkungstag drin "Wo hier der Sinn liegen soll....".

    Ich habe mir dabei gedacht fehlt noch der kleine Nebensatz. 🙂

    Siehe Dein Fragment/Posting
    Ich benutzte OpenOffice, was nicht alle Standardformen Rechtecke, Verbindungspfeile bereitstellt~nach dem "was": Was soll das heißen?~.



  • Mr. B schrieb:

    ich benutze OO, (KOMMA!) was nicht alle

    - standardkonformen Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen wie Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen, Rechtecke und Verbindungspfeile bereitstellt

    Ich denke, das zweite trifft es am besten. 🙂



  • @Mr.B Oder gehört an der Stelle kein Komma hin?



  • -predator- schrieb:

    Mr. B schrieb:

    ich benutze OO, (KOMMA!) was nicht alle

    - standardkonformen Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen wie Rechtecke und Verbindungspfeile bereitstellt

    ODER

    - Standardformen, Rechtecke und Verbindungspfeile bereitstellt

    Ich denke, das zweite trifft es am besten. 🙂

    Okay ich nehme jetzt das 2 und ersetze die alte Wortgruppe damit



  • @Mr.B und @-predator-

    Gesagt getan. Achja Ich bin ein schwieriger Fall 🙄 🤡

    So ich mache jetzt erstmal Kaffee 😋



  • jo, einigt euch. dann kann man mir ja die endgültige version dieses satzes zur überprüfung nochmals posten.

    Mr. B



  • Mr. B schrieb:

    jo, einigt euch. dann kann man mir ja die endgültige version dieses satzes zur überprüfung nochmals posten.

    Mr. B

    Ich habe die 2. Variante genommen. Kannst es jetzt nochmal prüfen. Danke 🙂



  • So, dann würde ich sagen: Fertig simma mit der Rechtschreibung...

    Mr. B



  • Mr. B schrieb:

    So, dann würde ich sagen: Fertig simma mit der Rechtschreibung...

    Mr. B

    So nachfolgend stelle ich den Beitrag neu ein und kennzeichen diesen Thread mit

    [X]

    Auf ein Neues.

    Mit Dank und Gruss von sclearscreen 🙂



  • RSS-Feed mit dem DataSet

    Autor: sclearscreen

    IDE: Visual Studio 2003 Standard

    Betriebssystem: WindowsXP

    Vorwort:

    Ganz kurz gesagt: Wer sich meinen vorherigen Beitrag angeschaut und das Projekt gezogen hat, wird beim Testen einen Bug feststellen.

    Es ist kein schwerwiegender Fehler, der den ganzen Desktop-PC zum Abstürzen bringt. Dieser Bug
    veranlasst auch nicht das Programm, sich ins System zu verabschieden. Nein, das Programm parst beim Umgang mit RSS-Feed, einer bestimmten Version, nicht alle Daten aus (sprich das Parsen des XML funktioniert nicht richtig). Bei dem Algorithmus zum Parsen des XML hatte ich voll auf Eigenbau gesetzt, was nicht ganz mit Erfolg gekrönt war. Soll ich als Autor dabei nun lachen oder weinen? Eher lachen - es zeigt einem, es geht besser, und zudem hat man gleich
    Stoff für einen neuen Beitrag.

    Also auf ans Werk.

    Inhalt:

    1 Refactoring des Programms
    1.1 Wo muss das Refactoring angesetzt werden?
    1.2 Was hat das Refactoring am Code verändert?
    2 Was lernt man daraus?
    3 Zum Testen gebe ich dem, der möchte, die Lösung noch mal auf den Weg
    3.1 Die Methode ohne Verwendung eines Proxyservers
    3.2 Die Methode mit Verwendung eines Proxyservers
    3.3 Verwenden kann man die zwei Methoden dann beispielsweise so
    4 Haftungsauschluss für etwaige Schäden an Hardware/Software des Users

    1.Refactoring des Programms
    1. 1 Wo muss das Refactoring angesetzt werden?

    Ich werde vorher mit Hilfe von UML den Weg der Daten bis zum Bug deutlich machen und die fehlerhafte Implementierung dieser Stelle dann aufzeigen. Sollte manches an der UML-Symbolik nicht ganz so stimmen, bitte ich das zu verschmerzen. Ich benutze OpenOffice was nicht alle Standardformen wie Rechtecke und Verbindungspfeile bereitstellt, um manche UML-Symbolik korrekt ausdrücken zu können. Daher ist es mir nicht möglich, spezielle Besonderheiten bei UML zu berücksichtigen. Beispielsweise kann man durch Verbindungspfeile
    kein asynchrones Verhalten bei Prozessaufrufen darstellen. Wo man einen Verbindungspfeil mit einer halben Pfeilspitze zur Darstellung bräuchte. So, genug abgeschweift, zur Sache.

    Wir beginnen mit einem Aktivitätendiagramm, was die Interaktion des Users mit seinem Reader kurz nach dem Start der Anwendung zeigt.

    Ich hoffe, jetzt fällt dem aufmerksamen Leser etwas auf, was uns die UML-Symbolik als Signal verkauft/darstellt! Dies ist eine Methode, die als Callback-Funktion für den Button genutzt wird.
    Und eben diese Callback-Funktion, stößt ihrerseits in ihrem Rumpf den Code zum Download und Parsen des Feeds an. Genau dort liegt der Hase im Pfeffer.
    In diesem Code interagieren 4 Klassenobjekte miteinander, um das XML-Script des RSS-Feeds per Http zu holen. Eines der Klassenobjekte ist eine Instanz des Typs XmlTextReader.
    Der Algorithmus des Parsens bestand nun darin, genau dieses Objekt mit Ablaufstrukturen sinnvoll zu kombinieren, um an die heiß begehrten item-Tags zu kommen.

    Genau dies passiert in folgendem Block:

    Die Implementierung zu diesem Block findet man in/unter folgender Methode (siehe Projekt aus meinem ersten Beitrag)

    private void button1_Click(object sender, System.EventArgs e)
    {
    	.
    	.
    	.
    }
    

    Innerhalb der Methode wird per Http über 2 Klassen

    WebRequest und WebResponse ein Stream-Objekt gebildet, was einem XmlTextReader gegeben wird.

    Der ganze Weg bis zur Stream-Gewinnung und der Instanzierung des XmlTextReader
    arbeitet normal und nach meiner Erwartung.

    XmlTextReader xmltextReader = new XmlTextReader(this.rssFeed.GetResponse().GetResponseStream());
    

    Nach dieser Zuweisung beginnt der Eigenbaualgorithmus zum Parsen des XML, der sich als zu unflexibel gegenüber dem RSS-Feed der Version 1.0 erweist.

    Zwei der der drei notwendigen Tags werden nicht ausgeparst. Es entsteht dabei auch keine Endlosschleife, die das GUI einfrieren lässt.

    Kurzerhand hatte ich mir einen Feed der Version 1.0 auf meine Platte geladen. Da die Klasse XmlTextReader auch aus Dateien lesen kann, habe ich die Instanzierung auf diese XML-Datei umgestellt. Somit konnte ich problemloser schrittweise debuggen, ohne auf das Netzwerk/Internet angewiesen zu sein.
    Dies hätte unter Umständen das Debuggen erschwert.

    Nach der Umstellung auf den nun lokalen Feed der Version 1.0 stellt man fest, dass der Algorithmus dann eben bei jedem „item-Tag“ nur einen der drei Tags ausparst. Hat der Algorithmus dies gemacht, springt dieser zum nächsten „item-Tag“ und vergisst somit einen Child-Knoten zu lesen. Dies genau ist besagter Bug.

    Ich gebe zu, an dem Punkt hatte mir dann die Muse gefehlt, um den Algorithmus zu verbessern. Die Gefahr einer Endlosschleife innerhalb der while-Schleife erachte ich zu groß.

    Eine einfacherer Lösung auf Basis von vorhandenen Komponenten ist immer besser, als das Rad weiter neu zu erfinden.

    Da ich wusste, dass die Klasse DataSet mit XML umgehen kann - gerade im Bezug auf Datenbanken -, versuchte ich 1 und 1 zusammenzuzählen.

    RSS-Feeds sind aufgrund ihrer internen Struktur datenzentriert, also ideal zur Arbeit mit Datenbanken, ausgelegt. Hinzu kommt, dass das DataSet XML sowohl laden als auch speichern kann; beim Laden kann es seine Daten aus einem Stream-Objekt bekommen und auch von einem XmlTextReader-Objekt.

    Also wenn sich diese Gedanken nicht gut anhören! Es ergab sich eine funktionsfähige Lösung innerhalb von 10-15 Minuten. Das Hilfesystem und die MSDN haben sich dabei wieder einmal bewährt.

    1.2 Was hat das Refactoring am Code verändert?

    Folgender Code wurde somit aus dem Projekt durch Auskommentieren verbannt:

    while(xmltextReader.Read() == true) 
    {
    	if(xmltextReader.Name == "item") 
    	{
    		while(xmltextReader.Read()) // weiterlesen im Element <ITEM>
    		{
    			switch(xmltextReader.Name) // lesen bis wir ....
    			{
    				case "title": 
    				{
    					if(titlereaded != true) 
    					{
    						while(xmltextReader.Read() == true && xmltextReader.NodeType != XmlNodeType.Text);
                            this.textBox2.Text = this.textBox2.Text + "TITLE: " + xmltextReader.Value + "\r\n"; 
    				        titlereaded = true; // merken uns, dass Titel gefunden ist
    					}
    					break;
    				}
    
    				case "link":
    				{
    					if(linkreaded != true) // doppeltes Lesen verhindern
    					{
    						while(xmltextReader.Read() == true && xmltextReader.NodeType != XmlNodeType.Text);
    				        this.textBox2.Text = this.textBox2.Text + "LINK: " + xmltextReader.Value + "\r\n"; 
    				        linkreaded = true; // merken uns, dass Link gefunden ist
    					}
    					break;
    				}
    
                                         // und auch das 3. Kernelement Description wurde gefunden 
    				case "description":
     		       {
                                                  // doppeltes Lesen der Description verhindern
    					if(descriptionreaded != true) 					
                        {
    						while(xmltextReader.Read() == true && xmltextReader.NodeType != XmlNodeType.Text);
    				        this.textBox2.Text = this.textBox2.Text + "DESCRIPTION: " + xmltextReader.Value + "\r\n"; 
    						descriptionreaded = true; // merken uns, dass Description ist
    					}
    					break;
    				}
    
    				default: break; // hier sind es optionale Tags des <ITEM> - Elements
    			}
    
    			// Wir prüfen jetzt, ob wir alle 3 Kernelemente des <ITEM> - Elements gefunden haben
    			if(titlereaded == true && linkreaded == true && descriptionreaded == true) //
    								break; // alles nötige für diesen einen News-Feed ist ausgelesen 		}
    		titlereaded = false;
    		linkreaded = false;
    		descriptionreaded = false;
    		this.textBox2.Text = this.textBox2.Text + "---------------------------------\r\n"; 
    	}
    }
    

    Das vorangegangene Listing wird ersetzt durch folgende kurze Sequenz. Ich war jedenfalls froh, dass dieses Geschoss vom Tisch war.

    DataSet ds                  = new DataSet("RSS-Feed: " + this.textBox1.Text);
    System.IO.Stream stream     = this.rssFeed.GetResponse().GetResponseStream();
    XmlTextReader xmltextReader = new XmlTextReader(stream);
    ds.ReadXml(xmltextReader);
    DataTable dt = ds.Tables["item"];
    

    Der neue Code liest sich auch viel besser. Und man bekommt auch einen Wink mit dem Zaunpfahl, was Wiederverwendung von getesteten Komponenten ausmacht.

    Und das bekommt man so ganz gratis mit dem Framework dazu. Eine Lösung mit 5 Zeilen Code gegenüber ich weiß nicht wie vielen Zeilen. Diese 5 Zeilen ersetzen die While-Schleife mit den ganzen eingeschachtelten Ablaufstrukturen.

    Man braucht sich jetzt wirklich nur ein DataGrid auf sein Formular ziehen, weist dessen Eigenschaft DataSource einer der Variablen ds oder dt zu und schon funktioniert beides.

    Im Verlauf der nächsten Sachen ,die man mit dem DataGrid anstellen kann, erinnert man sich
    an die Sache mit dem HitTestInfo. Da bekommt man angeklickte bzw. aktivierte Zellen im DataGrid
    zurück. Somit kommt man an den URL, der in der Spalte mit dem Namen „link“ im DataGrid auftauchen wird.

    Nachdem man durch folgenden Code...

    datagrid1.DataSource = dt;
    

    ... dem DataGrid das DataSet bekannt macht, ist man in der Lage, über HitTestInfo Informationen aus dem DataGrid zu holen.
    Man kann dem Browser also Informationen aus der Spalte mit
    dem Namen "Link" zuführen.

    Der Browser bietet ja durch das Browser-Control Interoperabilität mit Anwendungen. Das ist somit die ganze Kunst, um einen richtigen Feed-Reader zu bauen.

    2. Was lernt man daraus?

    a) Es ist immer gut und praktikabel, sich mit der Klassenbibliothek des Framework auseinander zu setzen. Es bringt bessere Lösungen.

    b) Man baut ausgiebig auf bereits getesten Code auf, was Wiederverwendbarkeit und Wartbarkeit
    fördert.

    c) Durch Verwendung bestehender getesteter Klassen wird Code sicherer, unter Umständen kürzer
    und auch lesbarer

    d) Die genannten Dinge bringen nur was, wenn man Kenntnisse über das jeweilige Framework hat.

    Man ist im Bezug auf die 4 Punkte immer gut beraten, die MSDN, Foren und das Internet zu benutzen.

    3. Zum Testen gebe ich dem, der möchte, die Lösung noch mal auf den Weg

    folgende Methode kann man in sein Projekt kopieren, einfach in die gewünschte Klasse als Methode reinkopieren. Existiert nicht ohne Grund in 2 Varianten!!!

    3.1 Die Methode ohne Verwendung eines Proxyserver

    // Achtung! Methode kann je nach Netzwerk, wenn ein Proxy zwischen euch sitzt, eine Exception werfen
    private DataTable GetRssFeedItems(string Url)
    {		
    	// Dieser Teil transferiert die Daten des RSS-Feeds zu unserem Rechner. Auch hier 
    	// könnte eine Exception auftreten, aber die Meldung bekommen wir auch in der Statuszeile
    	System.Net.HttpWebRequest rssFeed = (System.Net.HttpWebRequest)
                                                                             (WebRequest.Create(Url));
    
    	 // Der XmlTextReader bekommt einen Netzwerkstream direkt vom HttpWebRequest - Objekt
    	// Dieser Teil könnte eine Exception werfen. Das sehen wir dann aber in der Statuszeile		
    	DataSet ds = new DataSet("RSS-Feed: " + Url);
    	System.IO.Stream stream = rssFeed.GetResponse().GetResponseStream();
    	System.Xml.XmlTextReader xmltextReader = new System.Xml.XmlTextReader(stream);
    	ds.ReadXml(xmltextReader);
    	return (ds.Tables["item"] as DataTable);
          }
    

    3.2 Die Methode mit Verwendung eines Proxyserver

    private DataTable GetRssFeedItems(string Url, 
                                      string proxyAddresse,
                                      string proxyPort,
                                      string username,
                                      string passwort)
    {		
    	// Dieser Teil transferiert die Daten des RSS-Feeds zu unserem Rechner. Auch hier 
    	// könnte eine Exception auftreten, aber die Meldung bekommen wir auch in der Statuszeile
    	System.Net.HttpWebRequest rssFeed = (System.Net.HttpWebRequest)
                                                                             (WebRequest.Create(Url));
    
    	// ToDo: Wenn es hier eine Exception gibt, kann es daran liegen, dass ein Proxy zwischen uns
    	//       und dem Internet sitzt! Wir probieren es daher mit der Proxyauthentifizierung.
    	//       Man nehme diese Proxydaten aus dem lokalen Browser
    	rssFeed.Proxy = new System.Net.WebProxy(proxyAddresse + ":" + proxyPort);
    	rssFeed.Proxy.Credentials = new System.Net.NetworkCredential(username,passwort);
    
    	 // Der XmlTextReader bekommt einen Netzwerkstream direkt vom HttpWebRequest - Objekt
    	// Dieser Teil könnte eine Exception werfen. Das sehen wir dann aber in der Statuszeile		
    	DataSet ds = new DataSet("RSS-Feed: " + Url);
    	System.IO.Stream stream = rssFeed.GetResponse().GetResponseStream();
    	System.Xml.XmlTextReader xmltextReader = new System.Xml.XmlTextReader(stream);
    	ds.ReadXml(xmltextReader);
    	return (ds.Tables["item"] as DataTable);
          }
    

    3.3 Verwenden kann man die 2 Methoden dann beispielsweise so

    try // Versuch, den Stream normal zu holen
    	{
             		this.dataGrid1.DataSource = GetRssFeedItems("http://www.rss-verzeichnis.de/updates.xml");
    	}
    	catch(System.Exception ex) // Exception kommt, wenn ein Proxyserver dazwischen ist 
    	{
    		MessageBox.Show(ex.Message);
    
    		try // Versuch, den Stream zu holen trotz Proxy
    		{ 
    			this.dataGrid1.DataSource = GetRssFeedItems("http://www.rss-verzeichnis.de/updates.xml",
    						                    "100.100.100.100", // P.S.: fiktive ProxyIp
    						                    "8080",            
    							      "AlfonzDerUser",
    						                    "AfonzesPasswort"); 
    		}
    		catch(System.Exception ex)
    		{
    			// Punkt, an dem es keinen Sinn hat, die Anwendung weiter fortzusetzen
    			// Stream konnte mit normaler Internetverbindung nicht geholt werden
    			// Stream konnte auch nicht mit dem zwischengeschalteten Proxy beschaffen werden
    
    			MessageBox.Show(ex.Message);
    
    			// Gründe: 
    			//                 - falscher Proxy (Proxydaten, Proxyserver inaktiv)
    			//                 - Webserver mit RSS Feed inaktiv
    			//                 - Internetverbindung aus verschiedene Gruenden inaktiv 
    		}
    	}
    

    Wie man sieht, ist es ratsam, try/catch zu verwenden, wenn man die Methoden benutzt. Ich habs ja als Kommentar in der einen Metode angekündigt.

    4 Haftungsauschluss für etwaige Schäden an Hardware/Software des Users

    Man kann ja nie wissen! Ich will also hier am Ende noch mal darauf hinweisen, dass jeder, der Inhalte aus meinen Beiträgen nutzt, dies auf eigene Gefahr tut!
    Ich übernehme keinerlei Haftung für Schäden, die durch den Download, der Nutzung oder der Änderung an dem aufgeführten Quellcode aufgetreten sind.

    Ich teile gerne Erfahrungen, aber man muss sich absichern - gerade heute. So, das wäre auch geklärt, denke ich. Bleibt nur noch das Nachwort oder der Nachtrag.

    Nachtrag

    So, damit können Interessierte ihrem Spieltrieb freien Lauf lassen. Die Sache mit dem Mozillabrowser habe ich bewusst außen vor gelassen. Einen Browser noch reinzupacken, sollte kein Problem darstellen.
    Beispiele gibt es zuhauf im Netz. Außerdem: nur durch Rumspielen und Rumexperimentieren erlangt man Wissen.

    Beim DataGrid sollte man an die Sache mit dem HitTestInfo denken! Darüber gelangt man dann an den URL, der im Feed steckt, und kann somit ein Browser-Control mit URLs versorgen.

    Hier braucht man den URL dann nur noch mit der Navigate-Methode des Browserobjekts in Einklang zu bringen und schon hat man den RSS-Reader fertig. Daran lässt sich noch viel machen (Verwaltung von URLs auf die Feeds etc.).

    Den Beitrag als PDF

    http://www.c-plusplus.net/magazin/bilder/RSS/refactoring_RSS_Feed_mit_dem_DataSet.pdf


Anmelden zum Antworten