![]() |
RDF auslesen
Hi,
ich versuche gerade mit meinem Programm den RDF-Feed der DP ( ![]() Die XML-Datei wird auch korrekt runtergeladen, allerdings kann ich die Items nicht auswählen und ausgeben:
Delphi-Quellcode:
Die Listbox (lbTopics) ist aber einfach leer und xmlTopics.length ist 0. Was mache ich falsch?
procedure TfrmMain.UpdateList;
var sContent: string; i: integer; xmlTopics: IXMLDOMNodeList; begin sContent := idhDownloader.Get(IDS_FILENAME); xmlDoc.loadXML(sContent); // ShowMessage(sContent); if xmlDoc.parseError.errorCode <> 0 then begin MessageDlg('Fehler beim Laden des RDF-Feeds:'+#13#10 +'Die XML-Datei ist fehlerhaft! Bitte [email]chris@csd-software.net[/email] '+#13#10 +'kontaktieren.', mtError, [mbOK], 0); Close(); end; xmlTopics := xmlDoc.documentElement.selectNodes('/rdf/item'); for i := 0 to xmlTopics.length-1 do begin lbTopics.Items.Add(xmlTopics.item[i].selectSingleNode('title').text); end; end; Chris |
Re: RDF auslesen
Hi,
notgedrungen, habe ich das folgendermaßen gelöst:
Delphi-Quellcode:
Nun versuche ich noch dazu den ensprechenden Link finden:
procedure TfrmMain.UpdateList;
var sContent, sOut: string; i: integer; xmlTopics: IXMLDOMNodeList; begin lbTopics.Items.Clear; sContent := idhDownloader.Get(IDS_FILENAME); xmlDoc.loadXML(sContent); if xmlDoc.parseError.errorCode <> 0 then begin MessageDlg('Fehler beim Laden des RDF-Feeds:'+#13#10 +'Die XML-Datei ist fehlerhaft! Bitte [email]chris@csd-software.net[/email] '+#13#10 +'kontaktieren.', mtError, [mbOK], 0); Close(); end; xmlTopics := xmlDoc.documentElement.childNodes; for i := 2 to xmlTopics.length-1 do begin lbTopics.Items.Add(xmlTopics.item[i].childNodes.item[0].text); end; end;
Delphi-Quellcode:
Allerdings erhalte ich bei dir Zuweisung von sURL eine Zugriffsverletzung. Woran liegt das? Lt. dem
procedure TfrmMain.lbTopicsDblClick(Sender: TObject);
var sURL: string; begin if lbTopics.ItemIndex = -1 then Exit; sURL :=xmlDoc.documentElement.selectSingleNode('item[title="'+lbTopics.Items[lbTopics.ItemIndex]+'"]').text; if sURL <> '' then ShellExecute(self.Handle, 'open', PChar(sURL), nil, nil, SW_SHOW); end; ![]() Chris |
Re: RDF auslesen
Dass "selectNodes" und "selectSingleNode" nicht richtig funktionieren, liegt am verwendeten Namespace
Code:
Das zweite Attribut wird u.a. für die "item"-Knoten verwendet. Das siehst du auch, wenn du deinen Code zum Einlesen mal modifizierst:
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://my.netscape.com/rdf/simple/0.9/">
Delphi-Quellcode:
Hier werden die "item"-Knoten nämlich so angezeigt:
for i := 2 to xmlTopics.length-1 do
ShowMessage(xmlTopics.item[i].xml);
Code:
Da das aber kein Attribut im Sinne des Wortes ist (du kannst also nicht über "getAttribute" und Co. darauf zugreifen), deklarierst du nach dem Laden des XML-Codes einfach folgendes:
<item xmlns="http://my.netscape.com/rdf/simple/0.9/">
Delphi-Quellcode:
Wenn du mal schaust: Das unterscheidet sich nur durch das :na von der Angabe in der XML-Datei. Und genau dieses na (steht bei mir für name attribute) dient als Alias. (Du kannst stattdessen auch den Namen deiner Freundin oder was weiß ich angeben.) Zugegriffen wird dann wieder wie gehabt, wobei du den/das (?) Alias vor den gesuchten Knotennamen stellst:
xmldoc.setProperty('SelectionNamespaces',
'xmlns:na="http://my.netscape.com/rdf/simple/0.9/"');
Delphi-Quellcode:
Die Angabe des Alias ist, wie gesagt!, erforderlich, weil die Knoten sonst nicht gefunden werden.
xmlTopics := xmldoc.selectNodes('//na:item/na:title');
if(xmlTopics.length > 0) then for i := 0 to xmlTopics.length - 1 do lbTopics.Items.Add(xmlTopics.item[i].text); Dein Problem mit der Zugriffsverletzung bei der URL lässt sich auf ähnliche Art lösen, wobei ich aber vorschlagen möchte, dass du in jedem Fall den Umweg über einen Knoten (IXMLDOMNode) nimmst. Momentan entsteht die Zugriffsverletzung nämlich, weil "selectSingleNode" (auf Grund des Namespace) nichts findet, und du versuchst nun den Text eines nicht gefundenen Knotens an eine Variable zu übergeben. Das kann aber auch passieren, wenn du im Editor bspw. einen Knoten aus der XML-Datei entfernst und vergessen hast, dass das Programm noch eine zuvor geladene, vollständige Version des Dokumentes benutzt. Daher würde ich das also so machen:
Delphi-Quellcode:
(falls erforderlich musst du vorher wieder die Namespace-Eigenschaft setzen!)
node := xmldoc.selectSingleNode('//na:item[na:title="' +
lbTopics.Items[lbTopics.ItemIndex] + '"]/na:link'); if(node <> nil) then begin sUrl := node.text; ShellExecute(self.Handle,'open',pchar(sUrl),nil,nil,SW_SHOWNORMAL); end; |
Re: RDF auslesen
Hi Mathias,
danke für deine Erklärung. Ich werde das nachher mal ausprobieren und dann werde ich dir sagen können, ob es funktioniert. :) Chris *frühstücken geh* |
Re: RDF auslesen
Hi nochmal,
also: hat wunderbar funktioniert. Vielen Dank. Gibt es eine Möglichkeit mit XML herauszufinden, welcher dieser Namepsaces gesetzt ist bzw. werden muss? Chris |
Re: RDF auslesen
Gute Frage. Auslesen kannst du sie (zumindest die URLs) über IXMLDOMSchemaCollection (s. PSDK), bzw. auf die Schnelle:
Delphi-Quellcode:
Aber wie man sie jetzt so zuweist, dass man damit die entsprechenden Knoten findet ... :oops: ... da muss ich mich selbst erst mal damit beschäftigen. Oder wir fragen einfach mal sakura, bzw. einen anderen XML-Profi.
sch := xmldoc.Get_namespaces;
if(sch.length > 0) then for i := 0 to sch.length - 1 do ShowMessage(sch.namespaceURI[i]); |
Re: RDF auslesen
Hallo,
ich habe mich wegen dieses Threads zum ersten mal mit XMlDocument beschäftigt. Deshalb habe ich mir die Typebibliothek der Verion 5 (kam mit Office 2003) importiert (mit Komponentenwrapper deshalb steht im Sourc immer DefaultInterface). Damit habe ich dann versucht das Beispiel nachzuvollziehen. Hat auch geklappt.
Delphi-Quellcode:
Bei mir funktioniert es auch ohne
unit xml_main_frm;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, MSXML5_TLB, StdCtrls, OleServer; type TForm1 = class(TForm) Button1: TButton; ListBox1: TListBox; XMLDoc: TMSDOMDocument50; procedure Button1Click(Sender: TObject); procedure XMLDocondataavailable(Sender: TObject); private { Private-Deklarationen } procedure LoadXML_List(const Source : String); public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin ListBox1.Clear; LoadXML_List('http://www.delphipraxis.com/rdf.php'); end; procedure TForm1.LoadXML_List(const Source: String); begin XMLDoc.DefaultInterface.load(Source); end; procedure TForm1.XMLDocondataavailable(Sender: TObject); var xmlTopics : IXMLDOMNodeList; iCnt : Integer; jCnt : Integer; begin If XMLDoc.DefaultInterface.parseerror.errorCode=0 then begin // XMLDoc.DefaultInterface.SetProperty('SelectionNamespaces','xmlns:na="http://my.netscape.com/rdf/simple/0.9/"'); xmlTopics := xmlDoc.DefaultInterface.documentElement.childNodes; For iCnt := 0 to xmlTopics.length-1 do begin For jCnt:=0 to xmlTopics.item[iCnt].childNodes.length-1 do ListBox1.Items.Add(xmlTopics.item[iCnt].childNodes.item[jCnt].Text+' : '+ xmlTopics.item[iCnt].childNodes.item[jCnt].nodeName); ListBox1.Items.Add('----'); end; end else ShowMessage('Fehler beim parsen'); end; end.
Delphi-Quellcode:
D.h. mit
XMLDoc.DefaultInterface.SetProperty('SelectionNamespaces','xmlns:na="http://my.netscape.com/rdf/simple/0.9/"');
Delphi-Quellcode:
funktioniert es bei mir nicht. Sonderbar ???
XMLDoc.DefaultInterface.SetProperty('SelectionNamespaces','xmlns:na="http://my.netscape.com/rdf/simple/0.9/"');
|
Re: RDF auslesen
Hi,
nein denke ich nicht, weil es XML 5.0 ist. Ich denke, dass MSXML5 sich diese Daten selber herauszufinden versucht. Aber sicher bin ich mir nicht. Chris |
Re: RDF auslesen
@Jens: Das, was du hier machst
Delphi-Quellcode:
entspricht meiner Ansicht nach der Idee, die Chris am Anfang hatte. Du gehst damit zwar dem Namespace-Problem aus dem Weg und kannst (wenn du es eingrenzt) alle "item"-Knoten bzw. alle "title" auslesen, aber dazu musst du eben alles selbst durchlaufen.
xmlTopics := xmlDoc.DefaultInterface.documentElement.childNodes;
For iCnt := 0 to xmlTopics.length-1 do { ... } Einer der Vorteile, die bspw. der MS-XML-Parser bietet (nicht nur der, denke ich!), ist aber XPath. Das heißt, du kannst angeben, welche Bedingungen erfüllt sein müssen, und dann erhältst du eine Liste mit allen gefundenen Knoten, die diesen Bedingungen entsprechen. Vielleicht könnte man das (wenn man es nicht allzu wörtlich nimmt) mit dem SELECT-Statement von mySQL vergleichen?! Im Prinzip macht diese Anweisung:
Delphi-Quellcode:
das gleiche wie
xmlTopics := xmldoc.selectNodes('//item/title');
if(xmlTopics.length > 0) then for i := 0 to xmlTopics.length - 1 do lbTopics.Items.Add(xmlTopics.item[i].text);
Delphi-Quellcode:
wobei die zweite Variante einen Schönheitsfehler hat: ohne Prüfung wird grundsätzlich der erste Unterknoten benutzt
xmlTopics := xmldoc.documentElement.childNodes;
if(xmlTopics.length > 0) then for i := 0 to xmlTopics.length - 1 do if(xmlTopics.item[i].Get_nodeName = 'item') then lbTopics.Items.Add(xmlTopics.item[i].childNodes.item[0].text);
Delphi-Quellcode:
in der Hoffnung, dass dieser der "title" ist. ;) Normalerweise müsste man also noch prüfen, ob das auch zutrifft, und erst dann könnte/dürfte man die ermittelte Beschreibung in die Listbox eintragen.
xmlTopics[i].childNodes.item[0]
Da ist der Weg mit XPath doch weitaus kürzer, weil bei ihm sofort die "title"-Knoten ermittelt werden, die sich unter einem "item"-Knoten befinden. Oder nimm den Teil, bei dem die URL ermittelt werden soll. Als Referenz (zum Vergleich) dient der Titel (= die Beschreibung) aus der Listbox. Mit einer Schleife müsstest du also wieder durch alle Knoten durch, schauen ob es ein "item"-Knoten ist, prüfen ob der einen untergeordneten "title"-Knoten hat, vergleichen ob dessen Textinhalt mit dem selektierten Eintrag der Listbox identisch ist, und dann zu guter Letzt den "link"-Knoten und damit die URL ermitteln. Bei XPath genügt ein simples:
Delphi-Quellcode:
und du hast die URL.
node := xmldoc.selectSingleNode('//item[title="' +
lbTopics.Items[lbTopics.ItemIndex] + '"]/link'); if(node <> nil) then ShowMessage(node.text); Ein bisschen merkwürdig wird es in diesem speziellen Fall eben nur durch den Namespace. Im PSDK heißt es: SelectionNamespaces Property (Remarks) When an XML document contains elements defined in an external namespace, you must use this property to specify that namespace in order to use DOM methods such as selectNodes or selectSingleNode to navigate the document. @all: Ich habe durch wildes Herumprobieren folgende Variante, die unabhängig vom Namespace trotzdem alle Titel ausliest:
Delphi-Quellcode:
Etwas ... *hüstel* ... extrem wird´s beim Versuch, die URL zu ermitteln.
xmlTopics := xmldoc.selectNodes('//*[name(.)="item"]/*[name(.)="title"]');
Delphi-Quellcode:
:mrgreen:
node := xmldoc.selectSingleNode(
{ alle Knoten namens "item" } '//*[name(.)="item"]/' + { alle Unterknoten namens "text", deren Text mit dem Listbox-Eintrag identisch ist } '*[name(.)="title" and text()="' + lbTopics.Items[lbTopics.ItemIndex] + '"]' + { dann eine Ebene nach oben und alle (hoffentlich nur einen!) Knoten namens "link" } '/../*[name(.)="link"]'); if(node <> nil) then ShowMessage(node.text); Ich bezweifle, dass das eine vernünftige Variante ist. Dann sollte man sich wohl lieber mit dem Namespace auseinander setzen. Aber es funktioniert ... irgendwie ... ;) |
Re: RDF auslesen
@Matthias
Delphi-Quellcode:
verursacht bei mir eine stille Exception (wie auch immer das an dieser Stelle funktioniert). D.h. man merkt erst, das in der Zeile eine Exception auftritt, wenn man den Source in einen Try/Except Block kapselt.
XMLDoc.DefaultInterface.SetProperty('SelectionNamespaces','xmlns:na="http://my.netscape.com/rdf/simple/0.9/"');
Mit folgendem Source:
Delphi-Quellcode:
erhalte ich folgende Fehlermeldung:Versuch, einen schreibgeschützten Knoten zu ändern
procedure TForm1.XMLDocondataavailable(Sender: TObject);
var xmlTopics : IXMLDOMNodeList; iCnt : Integer; jCnt : Integer; begin If XMLDoc.DefaultInterface.parseerror.errorCode=0 then begin Try XMLDoc.DefaultInterface.SetProperty('SelectionNamespaces','xmlns:na="http://my.netscape.com/rdf/simple/0.9/"'); xmlTopics := xmlDoc.DefaultInterface.documentElement.childNodes; For iCnt := 0 to xmlTopics.length-1 do begin For jCnt:=0 to xmlTopics.item[iCnt].childNodes.length-1 do ListBox1.Items.Add(xmlTopics.item[iCnt].childNodes.item[jCnt].Text+' : '+ xmlTopics.item[iCnt].childNodes.item[jCnt].nodeName); ListBox1.Items.Add('----'); end; XMLDoc.DefaultInterface.save(ApplicationPath+'Test.xml'); Except On E : Exception do ShowMEssage(E.Message); end; end else begin ListBox1.Items.Add('Errorcode: '+IntToStr(XMLDoc.DefaultInterface.parseerror.errorCode)); ListBox1.Items.Add('Line: '+IntToStr(XMLDoc.DefaultInterface.parseerror.Line)); ListBox1.Items.Add('Char: '+IntToStr(XMLDoc.DefaultInterface.parseerror.LinePos)); ListBox1.Items.Add('Descritpion: '+(XMLDoc.DefaultInterface.parseerror.Reason)); end; end; Lustigerweise zweimal hintereinander |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:17 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz