![]() |
XML Find Node with Attribute
Hallo,
Ich hab das Forum nun schon rauf und runter durchsucht, irgendwie trete ich bei folgendem Problem auf der Stelle: Ich habe eine XML Datei:
Delphi-Quellcode:
Dieser XML Datei möchte ich Knoten einhängen. Die Knoten heißen immer <data></data> und unterscheiden sich nur durch ein Attribut und den Wert.
<datagrid />
Beispielsweise : <data cname="b_Downloadfile">C:\Software\binary.bin</data> Ich muss eine Hashtabelle durchlaufen welche zu jedem Namen auch einen Wert enthält. Ist eine Tabelle mit 2 Spalten in denen sich korrespondierende Paare befinden: b_Downloadfile <--->C:\Software\binary.bin c_Downloadfile <--->D:\Software\binary001.bin Wenn meine XML Datei noch leer ist, dann ist es relativ einfach da ich über addchild einfach ein Daten-Node erzeuge und die Werte zuweise. Wo ich nicht ganz durchsteige wie ich abprüfe ob es den entsprechenden Node nicht schon in meiner XML gibt. weil dann will ich ihn nicht neu einfügen sondern den Wert überschreiben. Mein Fortschritt bis dato:
Delphi-Quellcode:
ABer irgendwie steht da immer nur das letzte korrespondierende Wertpaar drinnen wenn ich die XML Datei dann öffne.
procedure TLogDlg.SaveStringGrid( const FileName: TFileName);
var f: TextFile; i, k,l: Integer; tagFound: Integer; xmlTag : String; XMLDocumentDataGrid: TXMLDocument; newNode: IXMLNode; begin l := 0; tagFound := 0; if FileExists(FileName)then begin XMLDocumentDataGrid := TXMLDocument.Create(Application); XMLDocumentDataGrid.FileName := FileName; XMLDocumentDataGrid.Active := true; // loop through cells -- Hashtabelle mit korrespondierenden Wertepaaren for i := 0 to LogDlg.dataGrid.RowCount - 1 do for k := 0 to LogDlg.dataGrid.ColCount - 1 do begin if (LogDlg.dataGrid.Cells[k, i] <> '')and (LogDlg.dataGrid.Cells[k+1, i] <> '') then begin //in xmlTag steht der Wert den das Attribut cname dann bekommen soll xmlTag := StringReplace(LogDlg.dataGrid.Cells[k, i], ' ', '', [rfReplaceAll, rfIgnoreCase]); newNode:=XMLDocumentDataGrid.DocumentElement.ChildNodes.FindNode('data'); while( newNode <> nil )do begin //data knoten schon vorhanden if(newNode.Attributes['cname'] = xmlTag)then begin //hier habe ich bedenken denn eigentlich will ich ja den Wert in XMLDocumentDataGrid setzen und nicht in newNode .... liegt hier das Problem? newNode.Text:=LogDlg.dataGrid.Cells[k+1, i]; tagFound := 1; Break; end else begin newNode := newNode.NextSibling; end; end; end; end; if (tagFound=0) then begin //no tag was found (XMLDocumentDataGrid.DocumentElement.AddChild('data').Attributes['cname']:=xmlTag); //hier müsste ich nun noch dem eben hinzugefügten Knoten einen Wert hinzuweisen. Leider kann ich nicht alles in Klammern setzen und .Text beschreiben, wie geht denn das? XMLDocumentDataGrid.SaveToFile(FileName); XMLDocumentDataGrid.Active := False; SaveStringGrid(FileName); end else begin XMLDocumentDataGrid.SaveToFile(FileName); XMLDocumentDataGrid.Active := False; end; end; end; was mir fehlt ist ein Befehl find Node with Name and Attribute Value :-)
Delphi-Quellcode:
Hat hier jemand eine Idee?
XMLDocumentDataGrid.DocumentElement.ChildNodes.FindNode('data').Attributes['cname'==xmlTag].Text :=LogDlg.dataGrid.Cells[k+1, i];
Wenn ich XPath verwende dann kann ich das mit INodeXml nicht machen oder? So ähnlich, nur ohne das .Value //data[starts-with(@cname,xmlTag)].Value Danke und schöne Grüße Andi |
Re: XML Find Node with Attribute
Du könntest das mit XPath lösen.
Mit XPath kann man wie mit einem Filter einen oder mehrere Knoten aus der Gesamtmenge rausholen. Beispiele für XPath-Ausdrücke: liefert alle Elemente namens "data", die das Attribut "cname" haben
Code:
liefert alle Elemente namens "data", bei denen das Attribut "cname" den Wert 123 hat.
//data[@cname]
Code:
Viele Parser haben XPath-Unterstützung. Die genaue Syntax ist von Parser abhängig.
//data[@cname="123"]
Delphi-Quellcode:
// Knoten mit XPath selektieren
nodelist := rootelement.SelectNodes('//data[@cname="123"]'); for i := 0 to nodelist.count-1 do // über alle Knoten begin node := nodelist.Item[i]; // z.B. wird hier ein neues Attribut gesetzt (node as IXMLElement).SetAttribute('idx', inttoStr(i)); end; |
Re: XML Find Node with Attribute
Hallo sx2008,
Danke für die schnelle Antwort. Dann versuche ich mal mein Glück mit SelectNodes. Eine Frage habe ich noch zu deinem Code:
Delphi-Quellcode:
Muss ich dann vielleicht so vorgehen:
// Knoten mit XPath selektieren
//hier wird doch eine KOPIE von rootelement."KnotenUnterMenge" angelegt oder? nodelist := rootelement.SelectNodes('//data[@cname="123"]'); for i := 0 to nodelist.count-1 do // über alle Knoten begin node := nodelist.Item[i]; //hier fügst du in node ein neues Attribut ein, wenn ich nun weiter unten //XMLDocumentDataGrid.SaveToFile(FileName); mache dann ist dort mein eingefügtes //Attribut nicht eingefügt oder? (node as IXMLElement).SetAttribute('idx', inttoStr(i)); end;
Delphi-Quellcode:
Aber geht das SetAttribute überhaupt anschließend an Select Nodes, weil es handelt sich ja um eine Nodelist und keinen einzelnen Node!??
rootelement.SelectNodes('//data[@cname="123"]').SetAttribute('idx', inttoStr(i));
Wenns den Knoten gibt füge ich einen aktuellen Wert als Attribut ein und wenns den Knoten nicht gibt erzeuge Ich ihn neu. Klingt gar nicht so kompliziert :-D muss ich mal testen und melde mich dann wieder. Schöne Grüße Andreas |
Re: XML Find Node with Attribute
Hallo,
Also dass scheint doch mega advanced zu sein :-) zumindest für mich. Nachdem ich mir ein paar Tutorials reingezogen hab (Teilweise sind die recht alt von 2003) bin ich immer noch nicht weitergekommen. Das SelectSingleNode kann ich nicht ausführen da ich mir kein
Delphi-Quellcode:
anlegen kann, was wiederum daran liegt, dass meine uses directive die UNITS msxml2 oder MSXML2_TLB nicht akzeptiert.
xmlDoc: IXMLDOMDocument2;
Von welchem Typ müsste denn rootelement sein? Um einfach meine schon implementierten IXMLNode zu benutzen habe ich mir hier aus dem Forum eine unit geholt: ![]() von Midiar für OpenXML ![]() Meine jetzt "verbesserte" Funktion stürtzt schon beim Laden des XML Files ab. Das Hatte mit TXML noch ganz gut funktioniert, aber dafür gibt es ja kein SelectSingleNode. VORHER
Delphi-Quellcode:
NACHHER
XMLDocumentDataGrid := TXMLDocument.Create(Application);
XMLDocumentDataGrid.FileName := FileName; XMLDocumentDataGrid.Active := true;
Delphi-Quellcode:
Was mach ich denn nur falsch?
procedure TForm1.SaveStringGrid( const FileName: TFileName);
var f: TextFile; i, k,l: Integer; tagFound: Integer; xmlTag,destFile : String; XMLDocumentDataGrid: IXMLDOMDocument; newNode,rootelement: IXMLNode; eintraege: IXMLDOMNodeList; subNode,node: IXMLDOMNode; xmlDoc: IXMLDOMDocument; nodelist: IXMLNodeList; begin l := 0; tagFound := 0; if FileExists(FileName)then begin //hier kommt schon der erste CRASH XMLDocumentDataGrid.loadXML(FileName) ; // loop through cells -- Hashtabelle mit korrespondierenden Wertepaaren for i := 0 to LogDlg.dataGrid.RowCount - 1 do for k := 0 to LogDlg.dataGrid.ColCount - 1 do begin if (LogDlg.dataGrid.Cells[k, i] <> '')and (LogDlg.dataGrid.Cells[k+1, i] <> '') then begin //in xmlTag steht der Wert den das Attribut cname dann bekommen soll xmlTag := StringReplace(LogDlg.dataGrid.Cells[k, i], ' ', '', [rfReplaceAll, rfIgnoreCase]); // Knoten mit XPath selektieren eintraege := XMLDocumentDataGrid.selectNodes('werte/eintrag/grenzwert[@benutzt="ja"]'); node := XMLDocumentDataGrid.DocumentElement.selectSingleNode('//data[@cname=xmlTag]'); if(node<>nil)then begin (node as IXMLNode).Text:=LogDlg.dataGrid.Cells[k+1, i]; end else begin // XMLDocumentDataGrid.DocumentElement.appendChild(subNode).attributes['cname']:=xmlTag; node := XMLDocumentDataGrid.DocumentElement.selectSingleNode('//data[@cname=xmlTag]'); node.Text:=LogDlg.dataGrid.Cells[k+1, i]; end; end; end; XMLDocumentDataGrid.save(FileName); // XMLDocumentDataGrid.Active := False; end; end; Danke und schöne Grüße Andi |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:16 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