2012-06-05 15 views
6

Ich habe eine XML in folgendem Format:Lese verschiedene Werte von XML mit XPath

<Accounts> 
    <Account Number="1" DebitAmount="1000" Amount="2827561.95" /> 
    <Account Number="225" DebitAmount="2000" Amount="12312.00" /> 
    <Account Number="236" DebitAmount="London" Amount="457656.00" /> 
    <Account Number="225" DebitAmount="London" Amount="23462.40" /> 
    <Account Number="236" DebitAmount="Bangalore" Amount="2345345.00" /> 
</Accounts> 

Wie retreive ich die einzigartigen Kontonummern mit XPath? dh ich will 1 die Werte erhalten, 225 und 236.

Dies ist, was ich getan habe: (Ich verwende Delphi 2007 ...)

Const XmlStr = 
' <Accounts> 
    <Account Number="1" DebitAmount="1000" Amount="2827561.95" /> 
    <Account Number="225" DebitAmount="2000" Amount="12312.00" /> 
    <Account Number="236" DebitAmount="London" Amount="457656.00" /> 
    <Account Number="225" DebitAmount="London" Amount="23462.40" /> 
    <Account Number="236" DebitAmount="Bangalore" Amount="2345345.00" /> 
</Accounts>'; 

function GetAccountNumbers:TList; 
Var 
    XMLDOMDocument : IXMLDOMDocument; 
    accounts : IXMLDOMNodeList; 
    accountdetail :IXMLDOMNode; 
    i:Integer 
    list :TList 
begin 
    Result:=TList.Create; 
    XMLDOMDocument:=CoDOMDocument.Create; 
    XMLDOMDocument.loadXML(XmlStr); 
    accounts:= XMLDOMDocument.SelectNodes(''./Accounts 
    /Account[not(@Number=preceding-sibling/ Account /@Number)]'); 
    for i := 0 to accountdetails.length - 1 do begin 
    accountdetail := accountdetails.item[i]; 
    //omitting the "<>nil" checks... 
    list.Add(accountdetail.attributes.getNamedItem('Number').Nodevalue; 
    end; 
end; 

Aber diese lieferte keine Knoten (accountdetails. Länge = 0). Bitte lassen Sie mich wissen, was ich hier vermisse.

Danke,

Pradeep

+4

Ist dieses Teil Teil Ihres echten Codes? Wenn man es anschaut, wird es nicht einmal kompilieren. – TLama

+0

Ich habe das kopiert; aber hat die Apostrophe nicht korrigiert. Ich wollte nur eine Vorstellung davon geben, was ich getan habe ... – Pradeep

+0

Um zu beantworten, warum Sie 'IXMLDOMNodeList' bekommen, ist die Null-Länge-Liste einfach. Das liegt daran, dass Sie versucht haben, den Fehler um die ungültigen '::' Zeichen von 'previous-sibling ::' zu umgehen, indem Sie ihn durch '/' ersetzen, aber das würde bedeuten, dass Sie den Wert mit dem Knoten 'previous-sibling 'vergleichen/.. 'was ist falsch. Sie müssen das 'vorhergehende Geschwister 'als Parameter verwenden (indem Sie' '' '' '' '' '' ') verwenden und den vorherigen Knoten nicht als konstanten Knotenwert ausgeben. Um die XPath-Achse zu verwenden, müssen Sie die neuere Version von MSXML verwenden, da die alte Version dies nicht unterstützt. – TLama

Antwort

5

Es scheint, dass die Version von MSXML von Delphi 2007 keine XPath-Achse unterstützt. Also, wenn Sie diese verwenden möchten versuchen, den folgenden Code, Import entweder Microsoft XML, v3.0 oder die Microsoft XML, v6.0 Typbibliothek und dann:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls, MSXML2_TLB; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

const 
    XMLString = 
    '<Accounts>' + 
    '<Account Number="1" DebitAmount="1000" Amount="2827561.95"/>' + 
    '<Account Number="225" DebitAmount="2000" Amount="12312.00"/>' + 
    '<Account Number="236" DebitAmount="London" Amount="457656.00"/>' + 
    '<Account Number="225" DebitAmount="London" Amount="23462.40"/>' + 
    '<Account Number="236" DebitAmount="Bangalore" Amount="2345345.00"/>' + 
    '</Accounts>'; 

type 
    TIntegerArray = array of Integer; 

function GetAccountNumbers(const AXMLString: string): TIntegerArray; 
var 
    I: Integer; 
    XMLDOMNodeList: IXMLDOMNodeList; 
    XMLDOMDocument: IXMLDOMDocument3; 
begin 
    XMLDOMDocument := CoDOMDocument60.Create; 
    if Assigned(XMLDOMDocument) and XMLDOMDocument.loadXML(AXMLString) then 
    begin 
    XMLDOMNodeList := XMLDOMDocument.selectNodes('/Accounts/Account[not(@Number=preceding-sibling::Account/@Number)]/@Number'); 
    SetLength(Result, XMLDOMNodeList.length); 
    for I := 0 to XMLDOMNodeList.length - 1 do 
     Result[I] := XMLDOMNodeList.item[I].nodeValue; 
    end; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    S: string; 
    I: Integer; 
    IntegerArray: TIntegerArray; 
begin 
    S := 'Account numbers: '; 
    IntegerArray := GetAccountNumbers(XMLString); 
    for I := 0 to Length(IntegerArray) - 1 do 
    S := S + IntToStr(IntegerArray[I]) + ', '; 
    Delete(S, Length(S) - 1, 2); 
    ShowMessage(S); 
end; 

end. 
+0

Ich habe die Microsoft XML 6.0 installiert, bekomme aber immer noch die Debugger-Ausnahme. (Ich bin jetzt daran interessiert, die Mehtod zu kennen) – Zeina

+0

@Zeina, du hast mir nicht so viel gesagt, welche Ausnahme und wann? Du meinst, wenn die Knoten vom XPath ausgewählt werden? Ich habe den Code von diesem Beitrag in Delphi 2007 unter Windows 7 und Windows XP ausprobiert und es funktioniert gut für mich. – TLama

+0

Dies ist die Nachricht: Ausnahmeklasse EOleException mit der Nachricht 'Erwartetes Token') 'gefunden': ' ./Accounts/Account[not(@Number=preceding-sibling-->:<--:Account/@Number)] /@Nummer'. Ist die Misrosoft XML nicht richtig installiert? – Zeina

0

diesen Code versuchen:

XMLDocument1: TXMLDocument; 

procedure TForm1.Button2Click(Sender: TObject); 
var 
    BodyNode:IXMLNode; 
    I: Integer; 
    sl:TStringList; 
begin 
    sl:=TStringList.Create; 
    XMLDocument1.Active:=False; 
    XMLDocument1.FileName:='C:\Zeina\Test.xml'; 
    XMLDocument1.Active:=True; 

    BodyNode:=XMLDocument1.DocumentElement; 
    for I:=0 to BodyNode.ChildNodes.Count-1 do 
    begin 
    if sl.IndexOf(BodyNode.ChildNodes[i].Attributes['Number'])=-1 then 
     sl.Add(BodyNode.ChildNodes[i].Attributes['Number']); 
    end; 
    Memo1.Lines:=sl; 
end; 

dies ist das Ergebnis:

+2

Es funktioniert zwar, aber es ist nicht der XPath Weg, was hier gefragt wurde. – TLama

-1

Ziena, ich Ihren Code ändern:

XMLDocument1: TXMLDocument; 

procedure TForm1.Button2Click(Sender: TObject); 
var 
    BodyNode:IXMLNode; 
    I: Integer; 
    sl:TStringList; 
begin 
    sl:=TStringList.Create; 
    XMLDocument1.Active:=False; 
    XMLDocument1.FileName:='C:\Zeina\Test.xml'; 
    XMLDocument1.Active:=True; 

    BodyNode:=XMLDocument1.DocumentElement; 
    sl.Duplicates := dupIgnore; 
    for I:=0 to BodyNode.ChildNodes.Count-1 do 
    sl.Add(
     BodyNode.ChildNodes[i].Attributes['Number']); // ignore duplicated lines 
    Memo1.Lines.AddStrings(sl); // for avoid memory leak 
end; 
+2

Und der XPath? – TLama

1

nicht genau verstehen, was Sie erreichen wollen. Vielleicht das?

'/Accounts/Account[not(@Number=preceding-sibling::node()/@Number)]/@Number' 
+0

Dies wird mit der alten Version der MSXML-Typ-Bibliothek im Lieferumfang von Delphi 2007 mit dem Fehler 'Exception-Klasse EOleException mit Nachricht' Erwartetes Token ') "gefunden" fehlschlagen: "./Accounts/Account [nicht (@ Nummer = Vorgänger-Geschwister ->: <-: Knoten()/@ Nummer)]. ' – TLama

+0

@TLama Als die neuere Typ-Bibliothek sollte verwendet werden, wie Sie in Ihrer Antwort vorgeschlagen . Es ist mir nicht klar, dass es in Delphi 2007 darum geht, xpath und axes zu verwenden, oder über den zu verwendenden xpath-Ausdruck? oder vielleicht beides? – balazs

+0

Ich suchte nach einer Lösung mit dem Xpath-Ausdruck, der in meiner Version von Delphi funktioniert ... – Pradeep

1

Dies funktioniert in meinem XPath example in Delphi XE2 und ein Delphi 2007 update of the XPath example gibt diese Ausgabe:

nodeName[0]:Number 
nodeValue[0]:1 
nodeName[1]:Number 
nodeValue[1]:225 
nodeName[2]:Number 
nodeValue[2]:236 

Die Ideen im Beispiel sollten Sie mit Delphi und 2007 entweder mit dem MSXML 6 DOM oder den OpenXML DOM erhalten gehen (Delphi XE2 unterstützt MSXML 6 DOM oder ADOM XML v4 DOM).

Beachten Sie, dass das Verhalten von MSXML 6 stark von der tatsächlich installierten Version abhängt, die von Ihrem Windows-Betriebssystem abhängt (daher this answer).

Das bedeutet wahrscheinlich, dass Sie nicht die neueste MS XML 6 installiert haben oder die richtigen Typbibliotheken nicht importiert haben (die Delphi 2007 msxml-Einheit enthält nicht IXMLDOMDocument2, die Sie für die XPath-Unterstützung benötigen).

Am 27. Juli 2012 habe ich mir etwas Zeit genommen, das Beispiel an Delphi 2007 anzupassen und es zu testen.
Ich bin nicht ganz sicher, dass es vollständig Speicherleck frei ist (Ich habe eine TDictionary-Klasse zusammengehackt, um Schnittstellen zu setzen), aber die Ergebnisse sind die gleichen wie das Delphi XE2-Beispiel und auf den ersten Blick sieht es ganz OK.

Laden Sie in der Demo-App XPathTester Beispiel 3 und führen Sie dann den XPath aus (der Ihr Beispiel lädt und den XPath ausführt).

Beachten Sie, dass die meisten anderen Dinge in der bo library mindestens Delphi 2009 erfordert, aber dieser Teil funktioniert.
In den nächsten Wochen werde ich es testen, wie ich es in einem anderen Projekt benötige, das immer noch bei Delphi 2007 ist.

procedure TMainForm.LoadXmlExample3ButtonClick(Sender: TObject); 
begin 
    LoadXmlExample([ // unique account numbers 
    '/Accounts/Account[not(@Number=preceding-sibling::Account/@Number)]/@Number' 
    ], 
    [ 
    '<?xml version="1.0"?>', 
    '<Accounts>', 
    ' <Account Number="1" DebitAmount="1000" Amount="2827561.95" />', 
    ' <Account Number="225" DebitAmount="2000" Amount="12312.00" />', 
    ' <Account Number="236" DebitAmount="London" Amount="457656.00" />', 
    ' <Account Number="225" DebitAmount="London" Amount="23462.40" />', 
    ' <Account Number="236" DebitAmount="Bangalore" Amount="2345345.00" />', 
    '</Accounts>' 
    ]); 
end; 
+0

Danke für die Lösung, aber das wird nicht funktionieren in meiner Version von Delphi .. – Pradeep

+0

Derzeit ist meine Antwort zu beweisen, dass die XPath-Abfrage arbeitet am XML mit MSXML 6. Also ja: Das sollte auch in Delphi 2007 möglich sein. Wenn ich Zeit finde, das Beispielprojekt nach Delphi 2007 zu übersetzen, werde ich Sie durch eine Bearbeitung dieser Antwort informieren. –

+0

@ user466744 Ich hatte endlich die Chance, an der Portierung der XPath-Demo mit Delphi 2007 zu arbeiten. Bitte sehen Sie, ob Sie den Code ausführen können, der hier gespeichert ist: http://bo.codeplex.com/SourceControl/changeset/80654 –