Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   XML parsen, aber wie (https://www.delphipraxis.net/192207-xml-parsen-aber-wie.html)

stifflersmom 29. Mär 2017 08:16

XML parsen, aber wie
 
Moin,

ich habe hier ein XML Ergebnis vom Bundesamt für Steuern und will eigentlich nur den Errorcode haben.
Wie würdet Ihr das auseinandernehmen, einen "vollständigen" XML-Parser benutzen oder versuchen mit Pos den Bereich des Errorcodes zu ermitteln?
Code:
<params>
<param>
<value><array><data>
<value><string>UstId_1</string></value>
<value><string>DE1234567889</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>ErrorCode</string></value>
<value><string>200</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>UstId_2</string></value>
<value><string>IT987654321</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Druck</string></value>
<value><string>nein</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_PLZ</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Ort</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Datum</string></value>
<value><string>28.03.2017</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>PLZ</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Ort</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Uhrzeit</string></value>
<value><string>15:08:27</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Name</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_ab</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Gueltig_bis</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Strasse</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Firmenname</string></value>
<value><string></string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Erg_Str</string></value>
<value><string></string></value>
</data></array></value>
</param>
</params>

Sherlock 29. Mär 2017 08:20

AW: XML parsen, aber wie
 
Wenn Du wirklich und für alle Zeiten nur den Errorcode brauchst, nimm halt Pos. Mit einem XML-Parser musst Du ja ähnlich vorgehen, nur halt mit anderem Zeug drumrum. Gibt es aber wirklich nur einen Errocode?

Sherlock

stifflersmom 29. Mär 2017 08:26

AW: XML parsen, aber wie
 
Ja, nur einen.
Der kann dann halt bummelig 25 verschiedene Werte aufweisen, aber das war es dann.

hoika 29. Mär 2017 08:37

AW: XML parsen, aber wie
 
Hallo,
das ist ja das betreffende Stück

// Startzeile
<value><string>ErrorCode</string></value>
<value><string>200</string></value>
// Endzeile
</data></array></value>

In dem Fall würde ich das ganze in eine StringList packen,
die durchlaufen bis zur Zeile <value><string>ErrorCode</string></value>
(Pos()=1)
danach den Index an eine zweite Methode übergeben, und die durchläuft die StringList dann bis zum </data></array></value>
und holt sich per StringReplace('<value><string>','') und StringReplace('</string></value>','') die Errorcodes

Quick&Dirty halt

Du solltest bloss vor dem Benutzen einer Zeile (Sche, Ersetzen) ein Trim machen.

uligerhardt 29. Mär 2017 08:39

AW: XML parsen, aber wie
 
Ich hab sowas Ähnliches mit OmniXML und XPath gelöst. Um z.B. aus einer *.groupproj-Datei zu lesen:
Delphi-Quellcode:
function GroupProj_ReadProjects(const AGroupProjName: string; ADProjNames: TStrings): Boolean;
var
  Doc: IXMLDocument;
  Root: IXMLElement;
  CallTargetNode, ProjectNode: IXMLNode;
  Targets: string;
begin
  Doc := CreateXMLDoc;
  Doc.PreserveWhiteSpace := False;

  if not Doc.Load(AGroupProjName) then
    Exit(False);

  Root := Doc.DocumentElement;
  if (Root = nil) or (Root.TagName <> 'Project') then
    Exit(False);

  CallTargetNode := Root.SelectSingleNode('Target[@Name=''Build'']/CallTarget');
  Targets := CallTargetNode.Attributes.GetNamedItem('Targets').Text;
  // usw. ...
end;

nahpets 29. Mär 2017 09:15

AW: XML parsen, aber wie
 
Ausgehend von diesem Post http://www.delphipraxis.net/1365797-post2.html

Statt:
Delphi-Quellcode:
  Try
     idHttp1 := TIdHTTP.Create;
     responseStream := TMemoryStream.Create;
     Screen.Cursor := crHourGlass;
    try
      IdHTTP1.Get(sUrlLong, responseStream);
      IdHTTP1.Disconnect(True);
    except on E: EIdException Do
      FormMain.add_info('UST Abfrage gescheitert '+E.Message);
    end;
    SetString(resultstring, PAnsiChar(responseStream.Memory), responseStream.Size);
   Finally
     idHttp1.Free;
     responseStream.Clear;
     responseStream.Free;
     Screen.Cursor := crDefault;
   End;
mache mal einfach das:
Delphi-Quellcode:
 
var
  sl : TStringList;
  i : Integer;
  sErrorCode : String;

begin
...
  Try
     idHttp1 := TIdHTTP.Create;
     sl := TStringList.Create;
     Screen.Cursor := crHourGlass;
    try
      sl.Text := IdHTTP1.Get(sUrlLong);
      IdHTTP1.Disconnect(True);
      i := sl.IndexOf('<value><string>ErrorCode</string></value>');
      if i > -1 then begin
        sErrorCode := sl[i + 1];
        sErrorCode := AnsiReplaceText(sErrorCode,'<value><string>','');
        sErrorCode := AnsiReplaceText(sErrorCode,'</string></value>','');
      end;
    except on E: EIdException Do
      FormMain.add_info('UST Abfrage gescheitert '+E.Message);
    end;
   Finally
     sl.Free;
     idHttp1.Free;
     Screen.Cursor := crDefault;
   End;

hoika 29. Mär 2017 11:03

AW: XML parsen, aber wie
 
Hallo Stefan,
damit bekommt er aber nur den ersten ErrorCode,
dass muss eine Schleife sein,
wenn ich das so wie unten verstanden habe.

<value><string>ErrorCode</string></value>
<value><string>200</string></value>
<value><string>300</string></value>
</data></array></value>

stifflersmom 29. Mär 2017 11:16

AW: XML parsen, aber wie
 
Nein, es gibt nur einmal einen Errorcode und von daher ist Nahpets Lösung sehr intzeressant.

Der schöne Günther 29. Mär 2017 11:21

AW: XML parsen, aber wie
 
Ich hoffe das kommt nicht falsch rüber, aber ihr durchsucht wirklich eine XML-Datei wie eine Text-Datei? Entscheidet sich die Gegenstelle einmal irgendwo ein Leerzeichen oder einen Zeilenumbruch woanders zu setzen und schon klappt nichts mehr.

Warum nicht über XML? Kostet doch kein Geld...

Lemmy 29. Mär 2017 11:25

AW: XML parsen, aber wie
 
XML Parser nehmen fertig. Kostet sicher weniger Entwicklungszeit als eine Textsuche, die auch auf sich ändernde Zeilenumbrüche reagieren kann!


Edit:@ Günther: Es ist echt von Vorteil Threads bis zum Ende zu lesen :-)

himitsu 29. Mär 2017 11:25

AW: XML parsen, aber wie
 
Warum versuchen eigentlich ständig Alle auf Biegen und Brechen nicht das zu verwenden, was dafür gedacht ist?
http://stackoverflow.com/questions/3...p-xml-response
http://www.office-loesung.de/ftopic312578_0_0_asc.php
http://howtoprogram.eu/question/n-a,55664

Bei einer XML auf die Idee zu kommen das mit sonstwas zu parsen ... braucht nur mal die Quelle die XML anders zu Formatieren und schon knallt es, obwohl sich am Inhalt garnichts ändert.
Gut, wer krank ist, der kann statt des fehleranfälligen Pos/Copy-Codes es mit einem etwas weniger fehleranfälligen RegEx versuchen. :roll:

Aber, auch wenn die Anwort eine XML ist, so ist das ganze eigentlich nur die Rückantwort eines Remote-Procedure-Call
Dokumentation: http://evatr.bff-online.de/eVatR/xmlrpc/
Beispiel: http://evatr.bff-online.de/evatrRPC?...trasse=&Druck=

Bei Google suchenDelphi XML RPC

p80286 29. Mär 2017 11:51

AW: XML parsen, aber wie
 
Nu komm mal runter.
Wer arbeitet sich in die Feinheiten von XML ein nur weil alle Jubeljahre mal (zuverlässig) eine Datei vorbei kommt. Insbesondere wenn die notorischen Besserwisser einem im Genick sitzen "das ist doch auch nur eine Textdatei, warum brauchen Sie dann so lange?"

Gruß
K-H

P.S.
Dank für die vielen Links, da ist die Mittagspause gerettet.

hoika 29. Mär 2017 15:12

AW: XML parsen, aber wie
 
Hallo,
Zitat:

Bei einer XML auf die Idee zu kommen das mit sonstwas zu parsen ... braucht nur mal die Quelle die XML anders zu Formatieren und schon knallt es
wenn in einer Xml, die ich selber schreibe, ich das Vorhandensein eines bestimmten Strings feststellen will,
nehme ich eine StringList und Pos, warum auch nicht?
Wenn's funktioniert?

Das war jetzt hier als Bsp., und nicht für die Xml des TE, die kommt ja aus der "Fremde".

nahpets 29. Mär 2017 15:31

AW: XML parsen, aber wie
 
'ne abgewandelte Version für die, die Angst um veränderte Zeilenumbrüche haben:
Delphi-Quellcode:
var
  sl : TStringList;
  s : String;
  i : Integer;
  sErrorCode : String;

begin
...
  Try
     idHttp1 := TIdHTTP.Create;
     sl := TStringList.Create;
     Screen.Cursor := crHourGlass;
    try
      s := IdHTTP1.Get(sUrlLong);
      IdHTTP1.Disconnect(True);
      // Tags werden vom Inhalt getrennt, indem sie je Tag in eine Zeile kommen
      s := AnsiReplaceText(s,'<',#13#10 + '<');
      s := AnsiReplaceText(s,'>','>' + #13#10);
      sl.Text := s;
      // Tags brauchen wir nicht, ebensowenig wie Leerzeilen.
      for i := sl.Count - 1 downto 0 do begin  
        if (Copy(sl[i],1,1) = '<')
        or (sl[i] = '') then sl.Delete(i);
      end;
      i := sl.IndexOf('ErrorCode');
      if i > -1 then sErrorCode := sl[i + 1];
    except on E: EIdException Do
      FormMain.add_info('UST Abfrage gescheitert ' + E.Message);
    end;
   Finally
     sl.Free;
     idHttp1.Free;
     Screen.Cursor := crDefault;
   End;
Und nun haben wir im Wechesel jeweil Beschreibung und den Text dazu.

Damit könnte man dann mit wenig Aufwand auch alles in 'ne Datenbank übernehmen.

Und ja, es geht auch mit 'nem XML-Parser.

bra 29. Mär 2017 15:41

AW: XML parsen, aber wie
 
Die Abfrage klingt irgendwie falsch:

Delphi-Quellcode:
      // Tags brauchen wir nicht, ebensowenig wie Leerzeilen.
      for i := sl.Count - 1 downto 0 do begin
        if (Copy(sl[i],1,1) = '<')
        or (Copy(sl[i],1,1) = '<')
        or (sl[i] = '') then sl.Delete(i);
      end;
Außerdem werden da auch die Endtags (/>) nicht berücksichtigt.

Ich würde auch einen XML-Parser nehmen. Ist initial zwar mehr Aufwand, aber wenn die Formatierung mal eine andere ist, funktioniert es noch.

Bei den ganzen Stringreplaces muss nur einmal anstatt <tag></tag> nur ein <tag /> stehen und es geht möglicherweise schon gar nicht mehr.

nahpets 29. Mär 2017 16:34

AW: XML parsen, aber wie
 
Oh, da war ein Schreibfehler drinne, muss latürnich
Delphi-Quellcode:
 // Tags brauchen wir nicht, ebensowenig wie Leerzeilen.
      for i := sl.Count - 1 downto 0 do begin
        if (Copy(sl[i],1,1) = '<')
        or (sl[i] = '') then sl.Delete(i);
      end;
heißen.

Alle Zeilen, die mit 'nem < beginnen, fliegen raus, ebenso alle Leerzeilen.

Endetags mussen hier nicht separat berücksichtigt werden, weil sie im XML, um das es hier geht, nicht vorkommen.

Wir haben hier immer mehr oder weniger viele öffnende Tags.
Dann den Inhalt.
Dann die entsprechende Anzahl schließender Tags.

Wenn nun vor die < und hinter die > jeweils ein Zeilenumbruch eingefügt wird, dann sind alle Tags separiert.

Sie und die ggfls. entstehenden Leerzeilen fliegen raus und übrig bleibt der Inhalt.

Natürlich ist das nicht auf beliebige XML-Dateien anwendbar, aber danach war auch nicht gefragt.

Das "Schöne" an dieser Vorgehensweise:

Sie funktioniert auch noch dann, wenn man fehlerhaftes XML bekommt, bei dem "richtige" Parser zu Recht streiken.

Mit dieser Vorgehensweise funktioniert seit Jahren meine Suchmaschine für HTML-Seiten. Es ist wurscht, ob die Seiten gültiges HTML enthalten und von Browsern/Parsern richtig oder näherungsweise interpretiert werden können.

Als Ergbenis erhalte ich immer ausschließlich den Text der Seiten und kann ihn, nach entsprechender Weiterverarbeitung, in den Suchindex eintragen.

hoika 29. Mär 2017 16:36

AW: XML parsen, aber wie
 
Hallo,
also, wenn das Format der Datei verschieden sein kann,
ist die Benutzung von TStringList falscher als ein Xlm-Parser.

Wer weiss, was die da alles machen, dass bekommt man mit TStringList gar nicht alles hin (unter Umständen).

haentschman 29. Mär 2017 17:21

AW: XML parsen, aber wie
 
Moin...:P

Ich wollte mich eigentlich heraushalten. :wink:

Was habt ihr gegen einen XML Parser? (:wink: ausgenommen die die das auch vorgeschlagen haben) Es gibt nicht nur die mit dem Overhead von TXMLDocument...:roll:

Beispiel (mit himXML):
Delphi-Quellcode:
uses
  himXML;
...
XML: TXMLFile;
...
XML := TXMLFile.Create;
...
XML.LoadFromFile(FFileName); //XML.LoadFromStream(Stream);
ErrorString := XML.Node['Errors'].Node['ErrorCode'].Text_S; // Beispiele für Node Bezeichnungen :-)
...fertsch. 8-)

hoika 29. Mär 2017 17:42

AW: XML parsen, aber wie
 
Hallo,
nicht wenn das so aussieht

<value><string>ErrorCode</string></value>
<value><string>200</string></value>
<value><string>300</string></value>
</data></array></value>

Oder müsste das nicht so aussehen?

<value><array><data>
<value><string>ErrorCode</string></value>
<value><string>200</string></value>
<value><string>ErrorCode</string></value>
<value><string>300</string></value>
</data></array></value

?

nahpets 29. Mär 2017 18:35

AW: XML parsen, aber wie
 
Es gibt in einer Antwort des Servers nur einen ErrorCode, nämlich den zu den abgefragten Daten.

Ein Satz kann immer nur einen Fehlercode enthalten sein.

Hier jetzt Probleme zu lösen, die nur dann auftreten, wenn man eine nicht der Spezifikation entsprechende Antwort bekommt, halte ich für übertrieben.

stifflersmom 29. Mär 2017 18:43

AW: XML parsen, aber wie
 
Ich hätte nicht gedacht, einen solchen Glaubenskrieg zu entfesseln....
Ich habe mich jetzt für eine sehr überschaubare Lösung mit Entfernen aller Zeilenumbrüche und nschließendem Pos entschieden, war halt einfach schnell zusammen getippt und da die "xml" Datei dieses feste Format aufweist, scheint mir diese Lösung auch durchaus gerechtfertigt zu sein.

Danke aber trotzdem für die vielen Vorschläge

sko1 30. Mär 2017 08:57

AW: XML parsen, aber wie
 
Zitat:

da die "xml" Datei dieses feste Format aufweist, scheint mir diese Lösung auch durchaus gerechtfertigt zu sein.
Bis das Bundesamt da mal an der Formatierung dreht...

Ciao
Stefan

sakura 30. Mär 2017 09:09

AW: XML parsen, aber wie
 
Zitat:

Zitat von himitsu (Beitrag 1365850)
Warum versuchen eigentlich ständig Alle auf Biegen und Brechen nicht das zu verwenden, was dafür gedacht ist?

Ich war schon leicht schockiert das hier alle mit Pos etc. arbeiten wollen. DANKE!

Ganz ehrlich, wenn man XML Daten hat, dann nutzt einen XML Parser, auch wenn diese selbst erstellt wurden. Vielleicht finden man ja doch mal einen Fehler, oder aktualisiert die Library, welche man nutzt... Und dann gibt es Probleme. Etwas anderes als einen XML Parser vorzuschlagen ist hier schlicht und ergreifend fahrlässig, insbesondere wenn schon im ersten Post klar wird, dass die Daten von extern kommen...

Und um auch noch einen Nutzen dazu zu bringen, wenn das XML Document geladen ist, ist hier die XPath-Query für die Fehlerbeschreibung:
Code:
//params/param[2]/value/array/data/value[1]/string
und den Fehlercode:
Code:
//params/param[2]/value/array/data/value[2]/string
...:cat:...

himitsu 30. Mär 2017 09:31

AW: XML parsen, aber wie
 
Und über einen passende RPC-Objekt-Bibliothek würde in etwa sowas rauskommen.
Delphi-Quellcode:
RPCObject := RPCHandler.GetIrgendwas('DE123456789', 'AB1234567890', '', '', '', ''); // UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck
UstId_1   := RPCObject.UstId_1;
ErrorCode := RPCObject.ErrorCode;

ioster 22. Jun 2021 11:51

AW: XML parsen, aber wie
 
Moin,

ich muss das hier angesprochene Ursprungsproblem mit der Auswertung der XML-RPC-Schnittstelle des Bundeszentralamtes für Steuern ebenfalls lösen. Bei Betrachtung der Schnittstellenbeschreibung hatte ich angenommen, dass es kein Hexenwerk ist. Nachdem ich nun über die Klippe von TLS 1.2 mit Indy gesprungen bin, suche ich nun nach der Auflösung des Rätsels, wie ich auf die einzelnen Parameter eines XML-Objektes zugreifen kann, wenn es keine klassischen Nodes sondern Params sind.

Die Code-Schnipsel, die ich bisher gefunden habe, helfen mir leider nicht weiter, weil ich die Methoden in meiner IDE nicht finden kann. Was hat es mit XPath auf sich und wie kann ich darüber auf die einzelnen Param-Einträge zugreifen?

So in etwa sieht das Dokument aus, das von der Schnittstelle zurückgegeben wird:
<params>
<param>
<value><array><data><value><string>UstId_1</string></value>
<value><string>DE123456789</string></value></data></array></value>
</param>
<param>
<value><array><data>
<value><string>ErrorCode</string></value>
<value><string>201</string></value></data></array></value>
</param>
<param>
<value><array><data><value><string>UstId_2</string></value>
<value><string>PL123456789</string></value></data></array></value>
</param>

Viele Grüße
Ingo

Edelfix 22. Jun 2021 12:23

AW: XML parsen, aber wie
 
Es ist doch normale Nodes die zufällig "params" heissen.

Codehunter 22. Jun 2021 13:30

AW: XML parsen, aber wie
 
Du solltest am besten einen Link posten wo man sich das XML in ganzer Schönheit anschauen kann.

ioster 22. Jun 2021 13:42

AW: XML parsen, aber wie
 
Zitat:

Zitat von Codehunter (Beitrag 1491375)
Du solltest am besten einen Link posten wo man sich das XML in ganzer Schönheit anschauen kann.

Der Aufbau wird auf Seite 1 dieses Threads dargestellt. Eine URL kann man nicht so ohne weiteres einstellen, weil die Abfrage eigentlich eine gültige Umsatzsteuer-Identnummer benötigt.

Testweise geht aber vielleicht dieser Link.

Viele Grüße
Ingo

ioster 22. Jun 2021 13:45

AW: XML parsen, aber wie
 
Ich komme über folgenden Code auf 16 Childnodes, die allesamt PARAM als heißen.

Delphi-Quellcode:
if not(XMLDok.IsEmptyDoc) then
  begin
    for AktNode := 0 to XMLDok.DocumentElement.ChildNodes.Count - 1 do
      begin
       :
      end;
  end;
Die Werte müsste ich doch nun über AttributeNodes lesen können oder wie muss ich mir das nun vorstellen?

Gruß
Ingo

DeddyH 22. Jun 2021 14:30

AW: XML parsen, aber wie
 
Ich würde je Knotenebene FindNode und NextSibling verwenden, um durch die Kindknoten derselben Ebene zu navigieren.

Edelfix 22. Jun 2021 14:33

AW: XML parsen, aber wie
 
Nicht vollständig aber als Hilfe:

Delphi-Quellcode:
procedure Test1(XMLStr: string);
var
  XmlDoc: IXmlDocument;
  Root: IXMLNode;
  s: string;
  I: Integer;
  N1: IXMLNode;
  N2: IXMLNode;
  N3: IXMLNode;
 
begin
  XMLDoc := TXMLDocument.Create(nil);
  try
    XMLDoc.XML.Text := XMLStr;
    XMLDoc.Active := true;
    Root := XmlDoc.DocumentElement;
    s := Root.NodeName; // Zum Debugen
    //--
   N1 := Root.ChildNodes.FindNode('params');
   for I := 0 to N1.ChildNodes.Count-1 do
   begin
     N2 := N1.ChildNodes[i];
     if N2.ChildNodes.FindNode('value')<>nil then
     begin
       if N2.ChildNodes.FindNode('array')<>nil then // Muss dann natürlich weiter verschachteln
      begin
        s := N2.ChildNodes.FindNode('array').Text;    
      end;
     end;
   end;
end;

TiGü 22. Jun 2021 14:59

AW: XML parsen, aber wie
 
Ich lege nach und mache ein vollständiges Beispiel:

Delphi-Quellcode:
unit GetUID;

interface

uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Net.HttpClient, System.Net.URLClient,
    System.Net.HttpClientComponent,
    Xml.xmldom, Xml.XMLDoc, Xml.XMLIntf,
    UID;

type
    TForm3 = class(TForm)
        btnSampleCall1: TButton;
        btnSampleCall2: TButton;
        NetHTTPClient1: TNetHTTPClient;
        procedure btnSampleCall1Click(Sender: TObject);
        procedure btnSampleCall2Click(Sender: TObject);
    private
        procedure ShowResponse(const URL: string);
    end;

var
    Form3: TForm3;

implementation

{$R *.dfm}

// https://evatr.bff-online.de/eVatR/xmlrpc/http
const
    SAMPLE1 = 'https://evatr.bff-online.de/evatrRPC?UstId_1=DE123456789&UstId_2=AB1234567890&Firmenname=&Ort=&PLZ=&Strasse=';
    SAMPLE2 = 'https://evatr.bff-online.de/evatrRPC?UstId_1=DE123456789&UstId_2=AB1234567890&Firmenname=Firmenname einschl. Rechtsform&Ort=Ort der Firma&PLZ=12345&Strasse=Strasse der Firma';

procedure TForm3.btnSampleCall1Click(Sender: TObject);
begin
    ShowResponse(SAMPLE1);
end;

procedure TForm3.btnSampleCall2Click(Sender: TObject);
begin
    ShowResponse(SAMPLE2);
end;

procedure TForm3.ShowResponse(const URL: string);
var
    Response: IHTTPResponse;
    XmlDoc: IXMLDocument;
    RPCAnswer: UID.IXMLParamsType;
    Param: UID.IXMLParamType;
    Value, Value2: UID.IXMLValueType;
    I, J, K: Integer;
    DbgStr: string;
begin
    Response := NetHTTPClient1.Get(URL);
    XmlDoc := NewXMLDocument();
    XmlDoc.LoadFromXML(Response.ContentAsString());
    RPCAnswer := Getparams(XmlDoc);
    DbgStr := '';
    for I := 0 to RPCAnswer.Count - 1 do
    begin
        Param := RPCAnswer.Param[I];

        DbgStr := DbgStr + 'Param: ' + I.ToString + Param.Value.String_ + sLineBreak;

        for J := 0 to Param.Value.Array_.Data.Count - 1 do
        begin
            Value := Param.Value.Array_.Data.Value[J];

            if Value.String_ <> '' then
                DbgStr := DbgStr + '....Value ' + J.ToString + ' string: ' + Value.String_ + sLineBreak;

            // weitere Schachtelungen möglich, hier aber nicht in den beiden Beispielen
            for K := 0 to Value.Array_.Data.Count - 1 do
            begin
                Value2 := Value.Array_.Data.Value[K];
                if Value2.String_ <> '' then
                    DbgStr := DbgStr + '.......Value ' + K.ToString + ' string: ' + Value2.String_ + sLineBreak;
            end;
        end;
    end;
    ShowMessage(DbgStr);
end;

end.
mit der importierten XML über den XML Data Binding Wizard (Name der Unit hier mit UID gewählt, kann aber geändert werden):

Delphi-Quellcode:

{***************************************************}
{                                                   }
{                 XML Data Binding                 }
{                                                   }
{         Generated on: 22.06.2021 13:59:22         }
{       Generated from: D:\Github-Repos\new 2.xml  }
{   Settings stored in: D:\Github-Repos\new 2.xdb  }
{                                                   }
{***************************************************}

unit UID;

interface

uses Xml.xmldom, Xml.XMLDoc, Xml.XMLIntf;

type

{ Forward Decls }

  IXMLParamsType = interface;
  IXMLParamType = interface;
  IXMLValueType = interface;
  IXMLArrayType = interface;
  IXMLDataType = interface;

{ IXMLParamsType }

  IXMLParamsType = interface(IXMLNodeCollection)
    ['{F262379E-19C8-4062-BEF9-78826C7E7FF0}']
    { Property Accessors }
    function Get_Param(Index: Integer): IXMLParamType;
    { Methods & Properties }
    function Add: IXMLParamType;
    function Insert(const Index: Integer): IXMLParamType;
    property Param[Index: Integer]: IXMLParamType read Get_Param; default;
  end;

{ IXMLParamType }

  IXMLParamType = interface(IXMLNode)
    ['{96DA74CC-1B8A-4368-AA9E-99464D25D589}']
    { Property Accessors }
    function Get_Value: IXMLValueType;
    { Methods & Properties }
    property Value: IXMLValueType read Get_Value;
  end;

{ IXMLValueType }

  IXMLValueType = interface(IXMLNode)
    ['{235C08FA-0EBB-41E4-A7FE-4123BFE0D250}']
    { Property Accessors }
    function Get_String_: UnicodeString;
    function Get_Array_: IXMLArrayType;
    procedure Set_String_(Value: UnicodeString);
    { Methods & Properties }
    property String_: UnicodeString read Get_String_ write Set_String_;
    property Array_: IXMLArrayType read Get_Array_;
  end;

{ IXMLArrayType }

  IXMLArrayType = interface(IXMLNode)
    ['{0A59A8A5-86DD-4D4B-B345-6D553493DC94}']
    { Property Accessors }
    function Get_Data: IXMLDataType;
    { Methods & Properties }
    property Data: IXMLDataType read Get_Data;
  end;

{ IXMLDataType }

  IXMLDataType = interface(IXMLNodeCollection)
    ['{B3867F76-837B-495B-AD2B-ACBCC3B50263}']
    { Property Accessors }
    function Get_Value(Index: Integer): IXMLValueType;
    { Methods & Properties }
    function Add: IXMLValueType;
    function Insert(const Index: Integer): IXMLValueType;
    property Value[Index: Integer]: IXMLValueType read Get_Value; default;
  end;

{ Forward Decls }

  TXMLParamsType = class;
  TXMLParamType = class;
  TXMLValueType = class;
  TXMLArrayType = class;
  TXMLDataType = class;

{ TXMLParamsType }

  TXMLParamsType = class(TXMLNodeCollection, IXMLParamsType)
  protected
    { IXMLParamsType }
    function Get_Param(Index: Integer): IXMLParamType;
    function Add: IXMLParamType;
    function Insert(const Index: Integer): IXMLParamType;
  public
    procedure AfterConstruction; override;
  end;

{ TXMLParamType }

  TXMLParamType = class(TXMLNode, IXMLParamType)
  protected
    { IXMLParamType }
    function Get_Value: IXMLValueType;
  public
    procedure AfterConstruction; override;
  end;

{ TXMLValueType }

  TXMLValueType = class(TXMLNode, IXMLValueType)
  protected
    { IXMLValueType }
    function Get_String_: UnicodeString;
    function Get_Array_: IXMLArrayType;
    procedure Set_String_(Value: UnicodeString);
  public
    procedure AfterConstruction; override;
  end;

{ TXMLArrayType }

  TXMLArrayType = class(TXMLNode, IXMLArrayType)
  protected
    { IXMLArrayType }
    function Get_Data: IXMLDataType;
  public
    procedure AfterConstruction; override;
  end;

{ TXMLDataType }

  TXMLDataType = class(TXMLNodeCollection, IXMLDataType)
  protected
    { IXMLDataType }
    function Get_Value(Index: Integer): IXMLValueType;
    function Add: IXMLValueType;
    function Insert(const Index: Integer): IXMLValueType;
  public
    procedure AfterConstruction; override;
  end;

{ Global Functions }

function Getparams(Doc: IXMLDocument): IXMLParamsType;
function Loadparams(const FileName: string): IXMLParamsType;
function Newparams: IXMLParamsType;

const
  TargetNamespace = '';

implementation

uses Xml.xmlutil;

{ Global Functions }

function Getparams(Doc: IXMLDocument): IXMLParamsType;
begin
  Result := Doc.GetDocBinding('params', TXMLParamsType, TargetNamespace) as IXMLParamsType;
end;

function Loadparams(const FileName: string): IXMLParamsType;
begin
  Result := LoadXMLDocument(FileName).GetDocBinding('params', TXMLParamsType, TargetNamespace) as IXMLParamsType;
end;

function Newparams: IXMLParamsType;
begin
  Result := NewXMLDocument.GetDocBinding('params', TXMLParamsType, TargetNamespace) as IXMLParamsType;
end;

{ TXMLParamsType }

procedure TXMLParamsType.AfterConstruction;
begin
  RegisterChildNode('param', TXMLParamType);
  ItemTag := 'param';
  ItemInterface := IXMLParamType;
  inherited;
end;

function TXMLParamsType.Get_Param(Index: Integer): IXMLParamType;
begin
  Result := List[Index] as IXMLParamType;
end;

function TXMLParamsType.Add: IXMLParamType;
begin
  Result := AddItem(-1) as IXMLParamType;
end;

function TXMLParamsType.Insert(const Index: Integer): IXMLParamType;
begin
  Result := AddItem(Index) as IXMLParamType;
end;

{ TXMLParamType }

procedure TXMLParamType.AfterConstruction;
begin
  RegisterChildNode('value', TXMLValueType);
  inherited;
end;

function TXMLParamType.Get_Value: IXMLValueType;
begin
  Result := ChildNodes['value'] as IXMLValueType;
end;

{ TXMLValueType }

procedure TXMLValueType.AfterConstruction;
begin
  RegisterChildNode('array', TXMLArrayType);
  inherited;
end;

function TXMLValueType.Get_String_: UnicodeString;
begin
  Result := ChildNodes['string'].Text;
end;

procedure TXMLValueType.Set_String_(Value: UnicodeString);
begin
  ChildNodes['string'].NodeValue := Value;
end;

function TXMLValueType.Get_Array_: IXMLArrayType;
begin
  Result := ChildNodes['array'] as IXMLArrayType;
end;

{ TXMLArrayType }

procedure TXMLArrayType.AfterConstruction;
begin
  RegisterChildNode('data', TXMLDataType);
  inherited;
end;

function TXMLArrayType.Get_Data: IXMLDataType;
begin
  Result := ChildNodes['data'] as IXMLDataType;
end;

{ TXMLDataType }

procedure TXMLDataType.AfterConstruction;
begin
  RegisterChildNode('value', TXMLValueType);
  ItemTag := 'value';
  ItemInterface := IXMLValueType;
  inherited;
end;

function TXMLDataType.Get_Value(Index: Integer): IXMLValueType;
begin
  Result := List[Index] as IXMLValueType;
end;

function TXMLDataType.Add: IXMLValueType;
begin
  Result := AddItem(-1) as IXMLValueType;
end;

function TXMLDataType.Insert(const Index: Integer): IXMLValueType;
begin
  Result := AddItem(Index) as IXMLValueType;
end;

end.
Ergibt bspw. für den zweiten Button:

Code:
[Window Title]
Getuidtest

[Content]
Param: 0
....Value 0 string: UstId_1
....Value 1 string: DE123456789
Param: 1
....Value 0 string: ErrorCode
....Value 1 string: 212
Param: 2
....Value 0 string: UstId_2
....Value 1 string: AB1234567890
Param: 3
....Value 0 string: Druck
....Value 1 string: nein
Param: 4
....Value 0 string: Erg_PLZ
Param: 5
....Value 0 string: Ort
....Value 1 string: Ort der Firma
Param: 6
....Value 0 string: Datum
....Value 1 string: 22.06.2021
Param: 7
....Value 0 string: PLZ
....Value 1 string: 12345
Param: 8
....Value 0 string: Erg_Ort
Param: 9
....Value 0 string: Uhrzeit
....Value 1 string: 14:59:16
Param: 10
....Value 0 string: Erg_Name
Param: 11
....Value 0 string: Gueltig_ab
Param: 12
....Value 0 string: Gueltig_bis
Param: 13
....Value 0 string: Strasse
....Value 1 string: Strasse der Firma
Param: 14
....Value 0 string: Firmenname
....Value 1 string: Firmenname einschl. Rechtsform
Param: 15
....Value 0 string: Erg_Str


[OK]

ioster 22. Jun 2021 15:04

AW: XML parsen, aber wie
 
Wenn ich die Antworten richtig verstehe, gibt es keine Möglichkeit, den String-Wert direkt von der obersten Param-Ebene zu lesen. Ich hatte mir schon eine For-Schleife konstruiert und bin nun auf Childnodes.First und Childnodes.NextSibling umgeschwenkt, um die oberste Ebene mit einer While-Schleife abzuarbeiten. Kommt im Endeffekt wohl auf dasselbe Ergebnis.

Bei Childnodes.Findnode muss ich immer wissen, wie der Name der nächsten Ebene lautet und kann eben nicht bis auf die unterste Ebene direkt zugreifen. Attributes kann ich dann wohl gänzlich aus dem Kreis der Lösungsansätze streichen.

Ich muss schauen, ob ich mir nicht eine rekursive Funktion baue, die mir das String-Paar aus der untersten Ebene nach oben zieht.

Gruß
Ingo

ioster 22. Jun 2021 15:13

AW: XML parsen, aber wie
 
Zitat:

Zitat von TiGü (Beitrag 1491384)
Ich lege nach und mache ein vollständiges Beispiel:

Delphi-Quellcode:
unit GetUID;

interface

uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Net.HttpClient, System.Net.URLClient,
    System.Net.HttpClientComponent,
    Xml.xmldom, Xml.XMLDoc, Xml.XMLIntf,
    UID;

type
    TForm3 = class(TForm)
        btnSampleCall1: TButton;
        btnSampleCall2: TButton;
        NetHTTPClient1: TNetHTTPClient;
        procedure btnSampleCall1Click(Sender: TObject);
        procedure btnSampleCall2Click(Sender: TObject);
    private
        procedure ShowResponse(const URL: string);
    end;

var
    Form3: TForm3;

implementation

{$R *.dfm}

// https://evatr.bff-online.de/eVatR/xmlrpc/http
const
    SAMPLE1 = 'https://evatr.bff-online.de/evatrRPC?UstId_1=DE123456789&UstId_2=AB1234567890&Firmenname=&Ort=&PLZ=&Strasse=';
    SAMPLE2 = 'https://evatr.bff-online.de/evatrRPC?UstId_1=DE123456789&UstId_2=AB1234567890&Firmenname=Firmenname einschl. Rechtsform&Ort=Ort der Firma&PLZ=12345&Strasse=Strasse der Firma';

procedure TForm3.btnSampleCall1Click(Sender: TObject);
begin
    ShowResponse(SAMPLE1);
end;

procedure TForm3.btnSampleCall2Click(Sender: TObject);
begin
    ShowResponse(SAMPLE2);
end;

procedure TForm3.ShowResponse(const URL: string);
var
    Response: IHTTPResponse;
    XmlDoc: IXMLDocument;
    RPCAnswer: UID.IXMLParamsType;
    Param: UID.IXMLParamType;
    Value, Value2: UID.IXMLValueType;
    I, J, K: Integer;
    DbgStr: string;
begin
    Response := NetHTTPClient1.Get(URL);
    XmlDoc := NewXMLDocument();
    XmlDoc.LoadFromXML(Response.ContentAsString());
    RPCAnswer := Getparams(XmlDoc);
    DbgStr := '';
    for I := 0 to RPCAnswer.Count - 1 do
    begin
        Param := RPCAnswer.Param[I];

        DbgStr := DbgStr + 'Param: ' + I.ToString + Param.Value.String_ + sLineBreak;

        for J := 0 to Param.Value.Array_.Data.Count - 1 do
        begin
            Value := Param.Value.Array_.Data.Value[J];

            if Value.String_ <> '' then
                DbgStr := DbgStr + '....Value ' + J.ToString + ' string: ' + Value.String_ + sLineBreak;

            // weitere Schachtelungen möglich, hier aber nicht in den beiden Beispielen
            for K := 0 to Value.Array_.Data.Count - 1 do
            begin
                Value2 := Value.Array_.Data.Value[K];
                if Value2.String_ <> '' then
                    DbgStr := DbgStr + '.......Value ' + K.ToString + ' string: ' + Value2.String_ + sLineBreak;
            end;
        end;
    end;
    ShowMessage(DbgStr);
end;

end.
mit der importierten XML über den XML Data Binding Wizard (Name der Unit hier mit UID gewählt, kann aber geändert werden):

Vielen Dank für den ausführlichen Code!! Ich bekomme nach den vielen Tipps aber immer mehr Fragezeichen auf der Stirn, weil ich die Objekte und Methoden überhaupt nicht verstehe und die Hilfe von Embarcadero auch nicht wirklich tiefschürfend ist.

Was hat es mit "RPCAnswer: UID.IXMLParamsType" auf sich? Und was ist der XML Data Binding Wizard und die generierte Datei mit den kryptischen Schlüsseln? Unter Umständen hilft dieser Thread ja künftig auch anderen, die sich mit den Zugriffsmöglichkeiten von Delphi auf ein XMLDocument beschäftigen müssen.

Viele Grüße
Ingo

TiGü 22. Jun 2021 17:15

AW: XML parsen, aber wie
 
Es ist eigentlich ganz stumpf.
Ich habe die Beispiel-XML von der Website abgespeichert und sie dann in Delphi über den besagten XML Data Binding Wizard importiert, der daraus Quelltext strickt.
http://docwiki.embarcadero.com/RADSt...rten_verwenden

Du schickst einen Request an den Service und erhälst synchron eine Antwort.
Dann lädst du aus der Antwort den String mit der XML in ein IXMLDocument und schmeißt das in die Funktion Getparams().
Zurückkonvertiert kommt dann das XML-Document als DOM-Baum, in dem du navigieren kannst.
Letztendlich gesehen sind das auch nur Wrapper mit den passenden Bezeichnern um das IXMLDocument herum.

TiGü 22. Jun 2021 17:16

AW: XML parsen, aber wie
 
Wenn du eher ein visueller Typ bist:
https://www.youtube.com/results?sear...L+Data+Binding

ioster 2. Jul 2021 09:01

AW: XML parsen, aber wie
 
Moin,

ich möchte mich an dieser Stelle bei allen bedanken, die mir über die Plattform wertvolle Tipps und Codebeispiele zum XML-Problem gesendet haben.

Die Überprüfung von USt-ID über das Bundeszentralamt für Steuern konnte ich damit erfolgreich implementieren und unter Umständen hilft mir das auch an anderer Stelle einmal weiter.

Viele Grüße
Ingo


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf