Teil 2: RSS-Newsfeeds mit Access erstellen

01.01.2007 von Helma  Spona
Im ersten Teil der Serie wurde der Aufbau einer RSS-Datei beschrieben und der VBA-Code so weit vorbereitet, dass nun nur noch die Inhalte der Datei erzeugt werden müssen. In diesem zweiten Teil der Artikelfolge geht es darum, wie Sie eine RSS-Datei mit Access ausgeben.

In unserem Beispiel wird eine Tabelle mit aktuellen Pressemitteilungen und Unternehmensnachrichten als Grundlage verwendet. Analog können Sie aber natürlich auch Sonderangebote aus Artikeldaten in die RSS-Datei ausgeben.

Die erforderliche Tabelle

Sie benötigen dazu zunächst einmal eine Tabelle, aus der die Daten stammen (Bild 1). Die Tabelle für den Beispielcode heißt tabNachrichten und besteht aus fünf Feldern.

Bild 1: Aufbau der Tabelle.

Die Daten abrufen

Um die Daten abzurufen, müssen Sie im Prinzip nichts weiter tun, als eine geeignete SQL-Anweisung auszuführen und dann die Daten zu durchlaufen. Für jeden Datensatz erzeugen Sie dann die Einträge im Channel- und im About-Block der Datei.

Im einfachsten Fall genügt eine einfache Funktion getInhalt, um die Daten aus der Datenbank abzurufen und als XML-Text zurückzugeben. Diesen brauchen Sie dann nur noch an den letzten Parameter der Prozedur RSSDateischreiben übergeben.

Innerhalb der Funktion getInhalt (Listing 1) deklarieren Sie dazu zunächst die notwendigen Variablen. Die wichtigste ist die Variable objRS, die das Recordset speichert.

Function getInhalt() As String
Dim strSQL As String
Dim objRS As ADODB.Recordset
Dim strTemp As String
Dim strListe As String

strTemp = ""
strListe = ""
Set objRS = New ADODB.Recordset
strSQL = "SELECT * FROM tabNachrichten"
objRS.Open strSQL, Application.CodeProject.Connection, adOpenStatic
If objRS.RecordCount > 0 Then
Do While objRS.EOF = False
If Not (objRS.Fields("Link").Value = Empty) Then
strTemp = strTemp & "<item rdf:about=""" & objRS.Fields("ID").Value & """>" & vbCrLf
strTemp = strTemp & "<link>" & objRS.Fields("Link").Value & "</link>" & vbCrLf
strListe = strListe & "<rdf:li resource=""" & objRS.Fields("ID").Value & """/>" & vbCrLf
Else
strTemp = strTemp & "<item rdf:about=""" & objRS.Fields("ID").Value & """>" & vbCrLf
strListe = strListe & "<rdf:li resource=""" & objRS.Fields("ID").Value & """/>" & vbCrLf
End If
strTemp = strTemp & "<title>" & objRS.Fields("Titel").Value & "</title>" & vbCrLf
strTemp = strTemp & "<description>" & objRS.Fields("Inhalt").Value & "</description>" & vbCrLf
strTemp = strTemp & "</item>" & vbCrLf
objRS.MoveNext
Loop
End If
objRS.Close
Set objRS = Nothing
strListe = "<items>" & vbCrLf & "<rdf:Seq>" & vbCrLf & strListe & "</rdf:Seq>" & vbCrLf & "</items>" & vbCrLf & "</channel>" & vbCrLf
getInhalt = strListe & strTemp
End Function

Erklärung Listing 1

Außerdem weisen Sie der Variablen strSQL den notwendigen SQL-Code zu. Hier werden alle Datensätze abgerufen, die in der Tabelle enthalten sind. Sie können natürlich auch eine SQL-Anweisung formulieren, die nur die letzten 10 oder 20 Einträge enthält.

Außerdem erzeugen Sie mit dem Schlüsselwort New ein neues RecordSet-Objekt. Dieses öffnen Sie dann anschließend mit der Open-Methode und übergeben die SQL-Anweisung als ersten Parameterwert.

Anschließend prüfen Sie mit der RecordCount-Eigenschaft, ob die Datensatzgruppe mindestens einen Datensatz enthält. Hierfür durchlaufen Sie die Datensätze mit einer Do-Schleife.

Innerhalb der Schleife müssen Sie zunächst prüfen, ob für den Datensatz im Feld Link ein Wert eingegeben wurde. Nur dann dürfen Sie nämlich das <link>-Element ausgeben. Als Wert für das Attribut rdf:about im <item>-Element geben Sie in beiden Fällen die ID des Datensatzes aus. Das stellt sicher, dass die Zuordnung zwischen Channel- und About-Block einwandfrei funktioniert. Um zu prüfen, ob der Link definiert wurde, vergleichen Sie den Feldwert einfach mit der Konstanten empty. Ist das Feld nicht leer, geben Sie anschließend das <link>-Element in die Variable aus. Am Ende der Schleife dürfen Sie natürlich nicht vergessen, die MoveNext-Methode aufzurufen, um zum nächsten Datensatz zu wechseln.

Abschlussarbeiten

Nach Abschluss der Schleife können Sie das Recordset schließen, indem Sie die Close-Methode aufrufen. Danach liegen zwei Teile des Inhalts in den Variablen strTemp und strListe vor (Listing 2 und 3). Damit ist der Inhalt jedoch noch nicht vollständig. Vielmehr müssen Sie noch den Inhalt der Variablen strListe in ein <rdf:Seq>-Element und dieses in ein <items>-Element einfassen.

Danach müssen Sie außerdem noch den schließenden <channel>-Tag anfügen. Beide Variablen hängen Sie dann aneinander und geben sie aus der Funktion zurück. Rufen Sie die Funktion dann wie folgt auf, wird die RSS-Datei korrekt erstellt (Bild 2).

<item rdf:about="1">
<link>http://localhost/Geschaeftsbericht.pdf</link>
<title>Deutliche Gewinnsteigerung</title>
<description>Die ABC-GmbH konnte ihren Gewinn im aktuellen Geschäftsjahr 2006 um mehr als 5% steigern und konnten daher Umsatz und Gewinn das fünfte Jahr in Folgeausbauen ...</description>
</item>
<item rdf:about="2">
<title>Personalwechsel im Vorstand</title>
<description>Zum Jahresende wird unser langjähriger Vorstandsvorsitzender Herr Dr. Mustermann unser Unternehmen verlassen und in seinen wohlverdienten Ruhestand gehen. Seine Nachfolge tritt seine bisherige Vertreterin Frau Dr. Müller an.</de-scription>
</item>

<rdf:li resource="1"/>
<rdf:li resource="2"/>

Bild 2:Darstellung der Datei im Sage RSS-Reader für Firefox.

Den Code optimieren

In dieser Form funktioniert der Code zwar, ist aber sicherlich nicht optimal. Probleme können sich vor allem dann ergeben, wenn sehr viele Datensätze mit langen Feldinhalten verarbeitet werden müssen. Durch das ständige Aneinanderhängen der Daten leidet die Performance. Zudem ist nicht auszuschließen, dass auch irgendwann die maximale Größe der String-Variablen überschritten wird.

Besser wäre daher, Sie würden die Funktion getInhalt in die Prozedur RSSDateischreiben integrieren. Optimal ist es dabei, wenn Sie anstelle des Parameters strInhalt einen Parameter strSQL definieren, mit dem Sie die SQL-Anweisung an die Prozedur übergeben. Das hat den Vorteil, dass Sie die Prozedur auch problemlos für andere Tabellen anwenden können, indem Sie einfach deren Felder in der SQL-Anweisung umbenennen.

Um die beiden Prozeduren in einer zusammenzufassen und alle Ausgaben direkt in die Textdatei statt zunächst in Variablen zu schreiben, müssen Sie allerdings das Recordset zweimal durchlaufen, einmal für den Channel-Block und ein zweites Mal für den About-Block - und das jeweils an der entsprechenden Stelle, an der die Ausgabe erfolgen muss (Listing 5).

Im Unterschied zur ersten Variante der Prozedur müssen Sie nun zuerst den Dateikopf in die Textdatei schreiben, bis zur Ausgabe des <description>-Elements im Channel-Block. Danach folgt die erste Abfrage der Daten, indem Sie das Recordset erstellen und füllen. In der Do-Schleife geben Sie zunächst alle <rdf:li>-Elemente aus. Anschließend schließen Sie das Recordset und geben die fehlenden Elemente des Channel-Blocks aus.

Nun folgt die zweite Datenabfrage. Diesmal geben Sie für jeden Datensatz das <item>-Element des About-Blocks aus. In diesem Fall sieht natürlich auch der Aufruf der Prozedur etwas anders aus (Listing 6).

Listing 5: Optimierte Prozedur RSSDateiSchreiben

Sub RSSDateiSchreiben2(strName As String, strPfad As String, strTitel As String, strBasisURL As String, strBeschreibung As String, strSQL As String)
Dim objFSO As New FileSystemObject
Dim objTS As TextStream
Dim strDateiname As String
'Dateikopf erzeugen
strDateiname = objFSO.BuildPath(strPfad, strName)
Set objTS = objFSO.CreateTextFile(strDateiname, True, True)
objTS.WriteLine "<?xml version=""1.0"" ?>"
objTS.WriteLine "<rdf:RDF xmlns:rdf=""http://www.w3.org/1999/02/22-rdf-syntax-ns#"" xmlns=""http://purl.org/rss/1.0/"">"
objTS.WriteLine "<channel rdf:about=""" & strBasisURL & "/" & strName & """>"
objTS.WriteLine "<title>" & strTitel & "</title>"
objTS.WriteLine "<link>" & strBasisURL & "</link>"
objTS.WriteLine "<description>" & strBeschreibung & "</description>"
'Daten für Channel-Block ermitteln
Dim objRS As ADODB.Recordset
Dim strTemp As String
Dim strListe As String
Set objRS = New ADODB.Recordset
objRS.Open strSQL, Application.CodeProject.Connection, adOpenStatic
If objRS.RecordCount > 0 Then
objTS.WriteLine "<items>" & vbCrLf & "<rdf:Seq>" & vbCrLf
Do While objRS.EOF = False
objTS.WriteLine "<rdf:li resource=""" & objRS.Fields("ID").Value & """/>" & vbCrLf
objRS.MoveNext
Loop
objTS.WriteLine "</rdf:Seq>" & vbCrLf & "</items>" & vbCrLf
End If
objTS.WriteLine "</channel>" & vbCrLf
objRS.Close
Set objRS = Nothing
strListe = "<items>" & vbCrLf & "<rdf:Seq>" & vbCrLf & strListe & "</rdf:Seq>" & vbCrLf & "</items>" & vbCrLf & "</channel>" & vbCrLf
'Inhalte für About-Block ermitteln
Set objRS = New ADODB.Recordset
objRS.Open strSQL, Application.CodeProject.Connection, adOpenStatic
If objRS.RecordCount > 0 Then
Do While objRS.EOF = False
If Not (objRS.Fields("Link").Value = Empty) Then
objTS.WriteLine "<item rdf:about=""" & objRS.Fields("ID").Value & """>" & vbCrLf
objTS.WriteLine "<link>" & objRS.Fields("Link").Value & "</link>" & vbCrLf
Else
objTS.WriteLine "<item rdf:about=""" & objRS.Fields("ID").Value & """>" & vbCrLf
End If
objTS.WriteLine "<title>" & objRS.Fields("Titel").Value & "</title>" & vbCrLf
objTS.WriteLine "<description>" & objRS.Fields("Inhalt").Value & "</description>" & vbCrLf
objTS.WriteLine "</item>" & vbCrLf
objRS.MoveNext
Loop
End If
objRS.Close
Set objRS = Nothing
'Datei abschließen
objTS.WriteLine "</rdf:RDF>"
objTS.Close
Set objFSO = Nothing
End Sub

Sub testen()
Dim strPfad As String
strPfad = getDBPfad()
RSSDateiSchreiben2 "Auto.rss", strPfad, "aktuelle Pressemeldungen", "http://www.meineurl.de", "Pressemeldungen", "SELECT * FROM tabNachrichten"
MsgBox "Fertig!"
End Sub

Fehlersuche

Die Ausgabe der Daten in die RSS-Datei funktioniert nun zwar problemlos, nur ergeben sich unter Umständen Fehler im XML-Code, wenn der Inhalt der Datenbank Sonderzeichen wie Größer- und Kleinerzeichen enthält. In diesem Fall erhalten Sie im RSS-Reader in der Regel einen Syntaxfehler und die RSS-Datei wird nicht gelesen und angezeigt.

Die Fehlersuche gestaltet sich dann natürlich vor allem bei längeren Dateien sehr schwierig. Suchen Sie mal in einer Datei aus über 1000 Zeilen nach einen Syntaxfehler, von dem Sie nicht einmal wissen, ob es ein fehlender oder ein nicht geschlossener Tag oder irgendetwas anderes ist. Das ist so gut wie aussichtslos.

Für die Fehlersuche gibt es aber einen kleinen Trick. Da es sich bei RSS-Dateien um XML-Dateien handelt, können Sie sie mit jedem XML-Parser parsen und somit auf korrekte XML-Syntax prüfen lassen. Wenn Sie keinen separaten XML-Parser zur Hand haben, verwenden Sie irgendeinen modernen Browser wie FireFox. Dann ist aber unbedingt zu beachten, dass Sie dem Browser mitteilen, dass er die Datei als XML parsen soll.

Dazu erstellen Sie einfach eine Kopie der RSS-Datei und benennen sie um. Vergeben Sie die Dateinamenserweiterung .XML. Nun können Sie die Datei im Browser öffnen, indem Sie beispielsweise in FireFox Datei/Datei öffnen aus dem Menü auswählen. FireFox zeigt Ihnen dann die Position des Fehlers an. So ist es sehr viel einfacher, Fehler zu finden.

Fehlerprüfung automatisieren

Mithilfe der MSXML-Bibliothek können Sie aber auch direkt nach Erzeugen der Daten prüfen, ob diese syntaktisch korrekt sind, sodass Sie nicht erst im RSS-Reader eine fehlerhafte Datei vorfinden.

Dazu müssen Sie zunächst einen Verweis auf die MSXML-Bibliothek erstellen:

1. Wählen Sie in der Entwicklungsumgebung Extras/Verweise aus.

2. Aktivieren Sie dann das Kontrollkästchen des Eintrags Microsoft XML, v? mit der höchsten Versionsnummer und schließen Sie den Dialog mit OK.

Nun fehlt nur noch eine kleine Funktion. Ihr übergeben Sie Name und Pfad der RSS-Datei. Die Funktion gibt dann einen booleschen Wert zurück, der angibt, ob die Datei fehlerfrei in das DOM des XML-Parsers geladen werden konnte (Listing 7).

Function boolPruefen(strDatei As String) As Boolean
Dim objXML As New MSXML2.DOMDocument
boolPruefen = objXML.Load(strDatei)
Set objXML = Nothing
End Function

Sie müssen dazu nichts weiter tun, als ein neues DOMDocument-Objekt zu erstellen und dessen Load-Methode aufzurufen. Ihr übergeben Sie den Pfad der zu öffnenden XML-Datei. Diese darf auch die Dateinamenserweiterung RSS haben. Die Methode gibt true zurück, wenn die Datei geladen werden konnte, oder false, wenn sie nicht geladen werden konnte. Das ist dann der Fall, wenn Syntaxfehler vorhanden sind.

Funktionsaufruf

Sie haben nun zwei Möglichkeiten, die Funktion aufzurufen: innerhalb der Prozedur RSSDateiSchreiben2 oder nach deren Aufruf. Letzteres hat den Vorteil, dass sie dann abhängig vom Ergebnis noch die Datei im Browser anzeigen können.

Allerdings haben Sie dann ein Problem. Sie kennen Dateinamen und Pfad der Datei nicht, weil dieser an die Prozedur übergeben und der Pfad erst innerhalb der Prozedur ergänzt wird. Das könnten Sie zwar hier noch einmal tun, müssen dann aber wieder ein FileSystemObject-Objekt erzeugen. Einfacher und performanter ist es daher, aus der Prozedur eine Funktion zu machen, die den Dateinamen mit Pfad zurückgibt (Listing 8).

Function RSSDateiSchreiben2(strName As String, strPfad As String, strTitel As String, strBasisURL As String, strBeschreibung As String, strSQL As String) As String
...
Set objFSO = Nothing
RSSDateiSchreiben2 = strDateiname
End Function

Nun brauchen Sie nur noch den Rückgabewert speichern und an die Funktion boolPruefen zu übergeben. Abhängig von Ergebnis der Prüfung können Sie dann eine Erfolgs- oder Fehlermeldung ausgeben, wie Listing 9 zeigt.

Sub testen()
Dim strPfad As String
Dim strDatei As String
Dim strDateiNeu As String
strPfad = getDBPfad()
strDatei = RSSDateiSchreiben2("Auto.rss", strPfad, "aktuelle Pressemeldungen", "http://www.meineurl.de", "Pressemeldungen", "SELECT * FROM tabNachrichten")
If boolPruefen(strDatei) = True Then
MsgBox "Fertig!"
Else
MsgBox "Die Datei ist fehlerhaft!"
End If
End Sub

Automatisch Internet Explorer starten

Sie können nun natürlich auch noch einen Schritt weiter gehen und bei einer fehlerhaften Datei direkt den Internet Explorer mit der Datei aufrufen. Dazu müssen Sie die Prozedur testen nur etwas erweitern (Listing 10).

Sub testen()
...
If boolPruefen(strDatei) = True Then
MsgBox "Fertig!"
Else
MsgBox "Die Datei ist fehlerhaft!"
'Kopieren
strDateiNeu = Mid(strDatei, 1, Len(strDatei) - 3) & "XML"
On Error Resume Next
Kill strDateiNeu
On Error GoTo 0
FileCopy strDatei, strDateiNeu
'Kopie im IE öffnen
Dim objIE As Object
Set objIE = CreateObject("InternetExplorer.Application")
objIE.navigate strDateiNeu
objIE.Visible = True
Set objIE = Nothing
End If
End Sub

Haben Sie festgestellt, dass die Datei fehlerhaft ist, kopieren Sie die Datei und versehen die Kopie mit der Dateinamenserweiterung .XML. Dazu müssen Sie zunächst eine möglicherweise alte Kopie der Datei mit Kill löschen und können dann die RSS-Datei mit FileCopy in die Datei mit der Endung .XML kopieren. Danach rufen Sie den Internet Explorer auf, indem Sie mit CreateObject aus der Klasse InternetExplorer.Application ein neues Objekt erzeugen. Dessen Navigate-Methode übergeben Sie dann den Dateinamen und blenden das Internet Explorer-Fenster mit der Visible-Eigenschaft ein. Das Ergebnis zeigt Bild 4.

Statt wie hier die späte Bindung zu verwenden, können Sie natürlich auch den Internet Explorer mit früher Bindung starten. Dazu müssten Sie aber auch hier wieder einen Verweis auf die Internet Explorer Bibliotheken erstellen.

Bild 4:Darstellung der fehlerhaften Datei im Internet Explorer.

Zusammenfassung

RSS-Dateien können mithilfe von Access schnell und unkompliziert erstellt werden. Die Inhalte sind variabel, sodass der vorgestellte Code auchsehr flexibel eingesetzt werden kann. Gerade für Softwareentwickler und Softwarefirmen ist RSS sicher ein interessantes Format. Mit eingebautem RSS-Reader in der eigenen Software könnte der Kunde so nicht nur über neue Updates und Versionen informiert werden, sondern auch der Support könnte erheblich einfacher und besser werden. (hs/mha)

Helma Spona <hs> studierte Wirtschaftswissenschaft mit Schwerpunkt Betriebsinformatik und eröffnete nach Abschluss ihres Studiums 1996 eine EDV-Beratung in Kerken, die sich auf Datenbankentwicklung und Webdesign spezialisiert hat. Außerdem schreibt sie regelmäßig Beiträge für verschiedene Zeitschriften sowie Bücher zu Programmier- und Datenbankthemen. Sie können sie über ihre Website www.helma-spona.de erreichen.