XML einlesen

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
wennerer
Beiträge: 524
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

XML einlesen

Beitrag von wennerer »

Hallo zusammen,
Ich möchte aus der in Lazarus befindlichen Datei packagesfiles.xml die Version eines Packages auslesen. Dazu habe ich hier schon einige Beiträge gefunden und gelesen (auch die Demos die bei Lazarus dabei sind). Es funktioniert sogar, aber ich glaube ich habe ein Monster erschaffen :D . Irgendwie muss das einfacher gehen. In einen anderen Beitrag schrieb wp_xyz das man das Rekursiv angehen muss, leider habe ich keine Ahnung wie das aussehen könnte.
Kann mir jemand auf die Sprünge helfen?

Viele Grüße
Bernd

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Laz2_DOM,
  Laz_XMLRead;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

function GetVersion : string;
var
   Document  : TXMLDocument;
   i, j,k,l,m: Integer;
   s         : string;

begin
  ReadXMLFile(Document,'packagefiles.xml');

  s:='';
  for i := 0 to (Document.DocumentElement.ChildNodes.Count - 1) do
   begin
    for j := 0 to (Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Count - 1) do
      for k := 0 to (Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Count - 1) do
        if Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[k].Attributes[0].NodeValue = 'Multis'
         then begin
          for l := 0 to (Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Count - 1) do
           if Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[l].NodeName = 'Version' then
            begin
             for m := 0 to Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[l].Attributes.Length -1 do
              begin
               s:=s+Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[l].Attributes[m].NodeValue;
               Result:= s;
              end;//Attributes
            end;//Version
         end;//if Multis
   end;//i

  Document.Free;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
 Label1.Caption:= 'Version: '+GetVersion;
end;

end.
Dateianhänge
project1.zip
(107.27 KiB) 23-mal heruntergeladen

Benutzeravatar
theo
Beiträge: 10498
Registriert: Mo 11. Sep 2006, 19:01

Re: XML einlesen

Beitrag von theo »


wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: XML einlesen

Beitrag von wp_xyz »

Also wenn ich mir den Link anschaue, dann bekomme ich den Verdacht, dass bei XPath dieselben Sadisten am Werk waren, die reguläre Ausdrücke erfunden haben...

Wie dem auch sei... Ich habe im Quellcode des Online-Package-Manager gespickt, der ja die Package-Versionen anzeigt, und habe gesehen, dass dort mit XMLConfig gearbeitet wird (so wie überall in der IDE beim Speichern von Config-Daten). Nach einigem Experimentieren mit dieser für mich neuen Klasse bin ich dann zu folgender einigermaßen kompakter Lösung gekommen:

Code: Alles auswählen

uses
  Laz2_XMLCfg;

const
  FILE_NAME = 'D:\ProgrammDaten\lazarus-config\lazarus-trunk_fpc-3.2.2\packagefiles.xml';

function GetPackageVersion(APackageName: String): String;
var
  xmlCfg: TXMLConfig;
  isLegacyList: Boolean;
  listCount: Integer;
  i: Integer;
  path: String;
  pkgName: String;
  majorVersion: String;
  minorVersion: String;
  release: String;
begin
  Result := '';
  xmlCfg := TXMLConfig.Create(nil);
  try
    xmlCfg.FileName := FILE_NAME;
    isLegacyList := xmlCfg.IsLegacyList('UserPkgLinks/');
    listCount := xmlCfg.GetListItemCount('UserPkgLinks/', 'Item', isLegacyList);
    for i := 0 to listCount-1 do
    begin
      path := xmlCfg.GetListItemXPath('UserPkgLinks/Item', i, isLegacyList, true);
      pkgName := xmlCfg.GetValue(path + '/Name/Value', '');
      if pkgName = APackageName then
      begin
        majorVersion := xmlCfg.GetValue(path + '/Version/Major', '0');
        minorVersion := xmlCfg.GetValue(path + '/Version/Minor', '0');
        release := xmlCfg.GetValue(path + '/Version/Release', '0');
        Result := format('%s.%s.%s', [majorVersion, minorVersion, release]);
        exit;
      end;
    end;
  finally
    xmlCfg.Free;
  end;
end;  

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetPackageVersion('callight_pkg'));
end;

Benutzeravatar
theo
Beiträge: 10498
Registriert: Mo 11. Sep 2006, 19:01

Re: XML einlesen

Beitrag von theo »

Ich bin auch kein Freund von Regex und gebe gerne zu, dass XPath ähnlich kryptisch ist, ABER es geht!
Hier das Bsp. für "Release":

Code: Alles auswählen

var
  Xml: TXMLDocument;
  XPathResult: TXPathVariable;
begin
  ReadXMLFile(Xml, 'packagefiles.xml');
  XPathResult := EvaluateXPathExpression('/CONFIG/UserPkgLinks//*[Name[@Value="Multis"]]/Version/@Release', Xml.DocumentElement);
  ShowMessage(String(XPathResult.AsText));
  XPathResult.Free;
  Xml.Free;
end;      
EDIT: Oder so, alles in einem Aufwasch:

Code: Alles auswählen

uses ..  DOM, XMLRead, XPath; 
...
procedure TForm1.Button1Click(Sender: TObject);
var
  Xml: TXMLDocument;
  XPathResult: TXPathVariable;
  APtr:Pointer;
begin
  ReadXMLFile(Xml, 'packagefiles.xml');
  XPathResult := EvaluateXPathExpression('/CONFIG/UserPkgLinks//*[Name[@Value="Multis"]]/Version/@*', Xml.DocumentElement);
  For APtr in XPathResult.AsNodeSet do
    ShowMessage(TDOMNode(APtr).NodeName+'='+TDOMNode(APtr).NodeValue);
  XPathResult.Free;
  Xml.Free;
end;      

wennerer
Beiträge: 524
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: XML einlesen

Beitrag von wennerer »

Herzlichen Dank für euere Antworten!
Ich muss mir das jetzt alles Erstmal in Ruhe ansehen damit ich (hoffentlich) verstehe was da steht.

Viele Grüße
Bernd

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: XML einlesen

Beitrag von fliegermichl »

Ich hab mal ein Beispiel gemacht, wie man es rekursiv machen kann.
Mein Beispiel hat ein Formular mit einem TOpenDialog, einem Button und einem TTreeView.
Wenn der Button angeklickt wird, wird der Opendialog aufgerufen und wenn der erfolgreich eine xml Datei liefert, wird der Inhalt der Datei rekursiv in den TreeView eingetragen.
Die Versionsabfrage kann dann mit Node.Attributes ... realisiert werden.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var xml : TXMLDocument;

  procedure ParseXML(TreeParent : TTreeNode; Node : TDomNode);
  var TN : TTreeNode;
  begin
    while (Assigned(Node)) do
    begin
      TN := Tree.Items.AddChild(TreeParent, Node.NodeName + ' ' + Node.NodeValue);
      ParseXML(TN, Node.FirstChild); // Das ist der rekursive Aufruf
      Node := Node.NextSibling;
    end;
  end;

begin
  if OpenDialog1.Execute then
  begin
    ReadXMLFile(xml, OpenDialog1.Filename);
    ParseXML(nil, xml.FirstChild);
    xml.Free;
  end;
end;
Dateianhänge
parsexml.zip
Beispiel TreeView rekursiv aus xml Datei befüllen
(139.62 KiB) 21-mal heruntergeladen

Mathias
Beiträge: 6207
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: XML einlesen

Beitrag von Mathias »

wennerer hat geschrieben:
Mi 26. Okt 2022, 18:20
Hallo zusammen,
Ich möchte aus der in Lazarus befindlichen Datei packagesfiles.xml die Version eines Packages auslesen. Dazu habe ich hier schon einige Beiträge gefunden und gelesen (auch die Demos die bei Lazarus dabei sind). Es funktioniert sogar, aber ich glaube ich habe ein Monster erschaffen :D . Irgendwie muss das einfacher gehen. In einen anderen Beitrag schrieb wp_xyz das man das Rekursiv angehen muss, leider habe ich keine Ahnung wie das aussehen könnte.
Kann mir jemand auf die Sprünge helfen?

Viele Grüße
Bernd

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Laz2_DOM,
  Laz_XMLRead;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

function GetVersion : string;
var
   Document  : TXMLDocument;
   i, j,k,l,m: Integer;
   s         : string;

begin
  ReadXMLFile(Document,'packagefiles.xml');

  s:='';
  for i := 0 to (Document.DocumentElement.ChildNodes.Count - 1) do
   begin
    for j := 0 to (Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Count - 1) do
      for k := 0 to (Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Count - 1) do
        if Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[k].Attributes[0].NodeValue = 'Multis'
         then begin
          for l := 0 to (Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Count - 1) do
           if Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[l].NodeName = 'Version' then
            begin
             for m := 0 to Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[l].Attributes.Length -1 do
              begin
               s:=s+Document.DocumentElement.ChildNodes.Item[i].ChildNodes.Item[j].ChildNodes.Item[l].Attributes[m].NodeValue;
               Result:= s;
              end;//Attributes
            end;//Version
         end;//if Multis
   end;//i

  Document.Free;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
 Label1.Caption:= 'Version: '+GetVersion;
end;

end.
Hast du es mal mit einem Klassischem "TXMLConfig" von der Unit "Laz2_XMLCfg;" probiert ?

Hier hatte ich dies mal angewendet:
https://github.com/sechshelme/Lazarus-E ... s_form.pas

Meine XML hat auch Itemsdurchnummerrierung wie die packagefiles.xml.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6207
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: XML einlesen

Beitrag von Mathias »

Ich habe ein bisschen rumprobiert und bin auf folgendes gekommen:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  XMLCFG: TXMLConfig;
  c, i: integer;
  s, ver: string;
begin
  Memo1.Clear;
  XMLCFG := TXMLConfig.Create(nil);
  XMLCFG.Filename := '/home/tux/fpcupdeluxe_stable/config_lazarus/packagefiles.xml';
  c := XMLCFG.GetValue('GlobalPkgLinks/Count', 10);

  Memo1.Lines.Add('PackagesCount: ' + c.ToString);
  Memo1.Lines.Add('');
  for i := 1 to c do begin
    s := 'GlobalPkgLinks/Item' + i.ToString + '/Name/Value';
    Memo1.Lines.Add('Name: ' + XMLCFG.GetValue(s, 'xxx'));

    s := 'GlobalPkgLinks/Item' + i.ToString + '/Version/Major';
    ver := 'Version: ' + XMLCFG.GetValue(s, '0') + '.';
    s := 'GlobalPkgLinks/Item' + i.ToString + '/Version/Minor';
    ver += XMLCFG.GetValue(s, '0') + '.';
    s := 'GlobalPkgLinks/Item' + i.ToString + '/Version/Release';
    ver += XMLCFG.GetValue(s, '0');
    Memo1.Lines.Add(ver);
    s := 'GlobalPkgLinks/Item' + i.ToString + '/LastUsed/Value';
    Memo1.Lines.Add('LastUsed: ' + XMLCFG.GetValue(s, ''));
    Memo1.Lines.Add('');
  end;
  XMLCFG.Free;
end; 
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: XML einlesen

Beitrag von wp_xyz »

Mathias hat geschrieben:
Sa 29. Okt 2022, 15:10
Ich habe ein bisschen rumprobiert und bin auf folgendes gekommen:
[...]
Das ist so ähnlich wie das was ich weiter oben schon gezeigt habe. Allerdings hat es den Nachteil, dass es eine laufende Node-Nummerierung voraussetzt. Das ist aktuell kein Problem, könnte aber eins werden: Vor nicht allzulanger Zeit hat man sich, aus Gründen, die ich nicht mehr weiß, entschieden, die Node-Nummerierung in lpi und lpk-Dateien zu entfernen - das hatte zur Folge, dass User von Laz 2.0.x oder älter beim Öffnen von mit Laz-trunk/main erstellten Projekten ein leeres Projekt zu sehen bekamen.

Der von mir verwendete Code kann auch mit nicht-nummerierten Nodes und fehlendem "Count"-Attribut umgehen. Ich hab's gerade ausprobiert.

Mathias
Beiträge: 6207
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: XML einlesen

Beitrag von Mathias »

Der von mir verwendete Code kann auch mit nicht-nummerierten Nodes und fehlendem "Count"-Attribut umgehen. Ich hab's gerade ausprobiert.
Für dies gibt es XMLCfg.GetChildCount.
Vor nicht allzulanger Zeit hat man sich, aus Gründen, die ich nicht mehr weiß, entschieden, die Node-Nummerierung in lpi und lpk-Dateien zu entfernen - das hatte zur Folge, dass User von Laz 2.0.x oder älter beim Öffnen von mit Laz-trunk/main erstellten Projekten ein leeres Projekt zu sehen bekamen.
Aus diesem Grund bin ich auf "GetChildCount" gestossen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
theo
Beiträge: 10498
Registriert: Mo 11. Sep 2006, 19:01

Re: XML einlesen

Beitrag von theo »

Ja, aber trotzdem: Wenn man den Trick raus hat, führt an XPath mMn für diesen Fall nichts vorbei.
Es ist am Ende nämlich doch leserlicher und verständlicher, als ein kompliziertes, "händisches" Durchrattern über viele Code Zeilen hinweg.

Code: Alles auswählen

XPathResult := EvaluateXPathExpression('/CONFIG/UserPkgLinks//*[Name[@Value="Multis"]]/Version/@Release', Xml.DocumentElement);


Fertsch!

wennerer
Beiträge: 524
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: XML einlesen

Beitrag von wennerer »

Nochmal vielen Dank an alle für euer Interesse und die Code Beispiele.
Das hilft mir sehr um mich in das Thema xml einzuarbeiten.

Viele Grüße
Bernd

Mathias
Beiträge: 6207
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: XML einlesen

Beitrag von Mathias »

Vor nicht allzulanger Zeit hat man sich, aus Gründen, die ich nicht mehr weiß,
Für automatisch generierte XML-Dateien sehen ich auch keinen grossen Vorteil.
Aber wen man selbst etwas in der XML modifizieren will, hat die fehlende Nummerierung einen Vorteil. Man kann Zeilen einfügen oder vertauschen, ohne das man sich um die Nummerierung kümmern muss.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten