Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Json Array of String: Wie Array-Items per Schleife auslesen (https://www.delphipraxis.net/213743-json-array-string-wie-array-items-per-schleife-auslesen.html)

juergen 18. Sep 2023 18:36

Json Array of String: Wie Array-Items per Schleife auslesen
 
Hallo,

mir fällt der Umgang mit Json noch ziemlich schwer. Ich habe 1 String-Array in der Json-Datei.
Folgendes Json (gekürzt).
Code:
{
    "Database": {
        "DB-ServerName": "",
        "DB-Name": "",
        "DB-User": "",
        "DB-User-Pw": 0,
        "DB-Provider": 0
    },
    "ShorthandSymbol": [
        "UxRT",
        "KuZT",
        "KpZR",
        "BFRS",
        "HKJD",
        "UDTR",
        "SI",
        "dgws",
        "DEFdg",
        "EIZ",
        "ITFd"
    ]
}
Ich habe den Json-Dateiinhalt in einem Json-Objekt:
Delphi-Quellcode:
...  
VAR
  my_jsonObj: TJSONObject;
BEGIN
 IF FileExists( ExtractFilePath( ParamStr( 0 ) ) + 'Settings.json' ) THEN
    BEGIN
      my_jsonObj := TJSONObject.ParseJSONValue( TFile.ReadAllText( ExtractFilePath( ParamStr( 0 ) ) + 'Settings.json' ) ) AS TJSONObject;
...
Jetzt muss ich irgendwie über eine Schleife (?) die einzelnen Stringwerte aus dem Array auslesen.

Wie komme ich an die einzelnen Json-String-Array-Werte?

Vielen Dank schon mal vorab.

Uwe Raabe 18. Sep 2023 19:13

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Mit
Delphi-Quellcode:
my_jsonObj.Values['ShorthandSymbol'] as TJSONArray
kommst du schon mal an das Array. Dort kannst du dann einfach über den Index auf die einzelnen Elemente zugreifen.

juergen 18. Sep 2023 22:06

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Vielen Dank, Uwe! :thumb:

Ich habe jetzt folgende Art einer Lösung:
Delphi-Quellcode:
ShowMessage( ( ( my_jsonObj.Values['ShorthandSymbol'] AS TJSONArray ).Items[1] ).ToString );
Das Ganze scheint zu funktionieren, aber irgendwie fühlt sich das für mich noch nicht richtig an.

mytbo 18. Sep 2023 23:12

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Zitat:

Zitat von juergen (Beitrag 1527087)
Das Ganze scheint zu funktionieren, aber irgendwie fühlt sich das für mich noch nicht richtig an.

Lösung mit mORMot. Wenn die Eigenschaftsnamen in der JSON-Datei nach den Regeln für Pascal Feldnamen benannt sind, lässt es sich automatisch mappen:
Delphi-Quellcode:
uses
  mormot.core.base,
  mormot.core.text,
  mormot.core.json,
  mormot.core.os;

type
  TDBSettings = packed record
    DataBase: record
      DBServerName: String; // statt "DB-ServerName"
      DBName: String;
      DBUser: String;
      DBUserPw: Integer;
      DBProvider: Integer;
    end;
    ShorthandSymbol: TStringDynArray; // array of String
    function LoadFromFile(const pmcFileName: TFileName): Boolean;
    procedure SaveToFile(const pmcFileName: TFileName);
  end;

function TDBSettings.LoadFromFile(const pmcFileName: TFileName): Boolean;
begin
  Result := RecordLoadJson(Self, StringFromFile(pmcFileName), TypeInfo(TDBSettings));
end;

procedure TDBSettings.SaveToFile(const pmcFileName: TFileName);
begin
  FileFromString(RecordSaveJson(Self, TypeInfo(TDBSettings)), pmcFileName);
end;
Im Beispiel ist das nicht der Fall, es muss ein Custom-Serializer registriert werden:
Delphi-Quellcode:
uses
  mormot.core.base,
  mormot.core.data,
  mormot.core.text,
  mormot.core.json,
  mormot.core.rtti,
  mormot.core.os;

type
  TDBSettingDataBase = class(TObject)
  private
    FDBServerName: String;
    FDBName: String;
    FDBUser: String;
    FDBUserPw: Integer;
    FDBProvider: Integer;
  protected
    class procedure ReadDataBaseJson(var pmvContext: TJsonParserContext; pmValue: TObject);
    class procedure WriteDataBaseJson(pmWriter: TJsonWriter; pmValue: TObject; pmOptions: TTextWriterWriteObjectOptions);
  public
    property DBServerName: String
      read FDBServerName write FDBServerName;
    property DBName: String
      read FDBName write FDBName;
    property DBUser: String
      read FDBUser write FDBUser;
    property DBUserPw: Integer
      read FDBUserPw write FDBUserPw;
    property DBProvider: Integer
      read FDBProvider write FDBProvider;
  end;

  TDBSettings = class(TSynAutoCreateFields)
  private
    FDataBase: TDBSettingDataBase;
    FShorthandSymbol: TStringDynArray;
  public
    function LoadFromFile(const pmcFileName: TFileName): Boolean;
    procedure SaveToFile(const pmcFileName: TFileName);
  published
    property DataBase: TDBSettingDataBase
      read FDataBase;
    property ShorthandSymbol: TStringDynArray
      read FShorthandSymbol;
  end;

class procedure TDBSettingDataBase.ReadDataBaseJson(var pmvContext: TJsonParserContext; pmValue: TObject);
var
  values: array[0..4] of TValuePUtf8Char;
begin
  if pmvContext.ParseObject(['DB-ServerName', 'DB-Name', 'DB-User', 'DB-User-Pw', 'DB-Provider'], @values) then
  begin
    with TDBSettingDataBase(pmValue) do
    begin
      DBServerName := values[0].ToString;
      DBName := values[1].ToString;
      DBUser := values[2].ToString;
      DBUserPw := values[3].ToInteger;
      DBProvider := values[4].ToInteger;
    end;
  end;
end;

class procedure TDBSettingDataBase.WriteDataBaseJson(pmWriter: TJsonWriter; pmValue: TObject; pmOptions: TTextWriterWriteObjectOptions);
begin
  with TDBSettingDataBase(pmValue) do
    pmWriter.AddJsonEscape(['DB-ServerName', DBServerName, 'DB-Name', DBName, 'DB-User', DBUser, 'DB-User-Pw', DBUserPw, 'DB-Provider', DBProvider]);
end;

function TDBSettings.LoadFromFile(const pmcFileName: TFileName): Boolean;
begin
  Result := ObjectLoadJson(Self, StringFromFile(pmcFileName));
end;

procedure TDBSettings.SaveToFile(const pmcFileName: TFileName);
begin
  FileFromString(ObjectToJson(Self), pmcFileName);
end;

initialization
  TRttiJson.RegisterCustomSerializerClass(TDBSettingDataBase, TDBSettingDataBase.ReadDataBaseJson, TDBSettingDataBase.WriteDataBaseJson);
Und so anwenden:
Delphi-Quellcode:
with TDBSettings.Create do
try
  LoadFromFile('settings.json');
  ShowMessage(DataBase.DBServerName);

  DataBase.DBServerName := 'TestServer';
  SaveToFile('settings.json');
finally
  Free;
end;
Bis bald...
Thomas

himitsu 18. Sep 2023 23:26

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Diese JSON-Klassen können auch sowas wie XPath.

https://docwiki.embarcadero.com/Libr...JSONPathParser
https://docwiki.embarcadero.com/Libr...ject.FindValue
...

mytbo 18. Sep 2023 23:39

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Zitat:

Zitat von himitsu (Beitrag 1527095)
Diese JSON-Klassen können auch sowas wie XPath.

Etwa so:
Delphi-Quellcode:
var
  doc: TDocVariantData;
begin
  doc.InitJsonFromFile('settings.json');
  ShowMessage(doc.P['DataBase.DB-ServerName']);
Bis bald...
Thomas

juergen 19. Sep 2023 16:53

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Zunächst vielen Dank!

@mytbo,
Je länger ich mir das anschaue, desto mehr gefällt es mir!:thumb:

Ich werde das heute Abend mal mit mORMot angehen.

mytbo 19. Sep 2023 17:37

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Zitat:

Zitat von juergen (Beitrag 1527113)
Ich werde das heute Abend mal mit mORMot angehen.

mORMot gehört zu den qualitativ hochwertigsten Bibliotheken im Pascal Universum. Es sind unzählige Klassen und Funktionen zu finden. Um schnell ein Erfolgserlebnis zu haben, sollte man sich zu Beginn nur auf einen Teilbereich konzentrieren. In deinem Beispiel die JSON Serialisierung. Die Units sind wohl geordnet und strukturell logisch aufgebaut. Wenn man es verstanden hat, fügt sich alles wunderbar zusammen. Nur der Anfang, der ist schwer.

Die mORMot Bibliothek muss nicht installiert werden. Den aktuellen Commit und die static binaries aus dem letzten Tag herunterladen. In Delphi die entsprechenden Bibliotheks- und Suchpfade setzen. Beim Erstellen hilft dieses Muster:
Code:
// Die Doppelpunkte einfach durch den Speicherpfad ersetzten
..\src;..\src\app;..\src\core;..\src\crypt;..\src\db;..\src\lib;..\src\misc;..\src\net;..\src\orm;..\src\rest;..\src\script;..\src\soa;..\src\tools\ecc;..\src\ui;
Die aktuelle Entwicklung ist mORMot2. mORMot ist gut dokumentiert. Die Hilfe bezieht sich noch auf mORMot1. Die Namen für Klassen und Funktionen können sich leicht unterscheiden. Sie umfasst mehr als 2500 Seiten. Davon enthalten die ersten ca. 650 Seiten einen sehr lesenswerten allgemeinen Teil, der Rest ist API Dokumentation.

Bis bald...
Thomas

mytbo 19. Sep 2023 18:43

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Um das Ganze abzuschließen, im konkreten Fall würde es reichen, die Eigenschaftsnamen für das Lesen und Schreiben der JSON-Datei zu ändern:
Delphi-Quellcode:
uses
  mormot.core.base,
  mormot.core.data,
  mormot.core.text,
  mormot.core.json,
  mormot.core.rtti,
  mormot.core.unicode,
  mormot.core.variants,
  mormot.core.os;
 
type
  TDBSettingDataBase = class(TSynPersistent)
  private
    FDBServerName: String;
    FDBName: String;
    FDBUser: String;
    FDBUserPw: Integer;
    FDBProvider: Integer;
  published
    property DBServerName: String
      read FDBServerName write FDBServerName;
    property DBName: String
      read FDBName write FDBName;
    property DBUser: String
      read FDBUser write FDBUser;
    property DBUserPw: Integer
      read FDBUserPw write FDBUserPw;
    property DBProvider: Integer
      read FDBProvider write FDBProvider;
  end;
 
  TDBSettings = class(TSynAutoCreateFields)
  private
    FDataBase: TDBSettingDataBase;
    FShorthandSymbol: TStringDynArray;
  public
    function LoadFromFile(const pmcFileName: TFileName): Boolean;
    procedure SaveToFile(const pmcFileName: TFileName);
  published
    property DataBase: TDBSettingDataBase
      read FDataBase;
    property ShorthandSymbol: TStringDynArray
      read FShorthandSymbol;
  end;

function TDBSettings.LoadFromFile(const pmcFileName: TFileName): Boolean;
begin
  Result := ObjectLoadJson(Self, StringFromFile(pmcFileName));
end;

procedure TDBSettings.SaveToFile(const pmcFileName: TFileName);
begin
  FileFromString(ObjectToJson(Self), pmcFileName);
end;
 
initialization
  Rtti.ByClass[TDBSettingDataBase].Props.NameChanges(
    ['DBServerName', 'DBName', 'DBUser', 'DBUserPw', 'DBProvider'],
    ['DB-ServerName', 'DB-Name', 'DB-User', 'DB-User-Pw', 'DB-Provider']);
Als Ausführung mit Records wie folgt:
Delphi-Quellcode:
type
  TDBSettingDataBase = packed record
    DBServerName: String;
    DBName: String;
    DBUser: String;
    DBUserPw: Integer;
    DBProvider: Integer;
  end;

  TDBSettings = packed record
    DataBase: TDBSettingDataBase;
    ShorthandSymbol: TStringDynArray; // array of String
    function LoadFromFile(const pmcFileName: TFileName): Boolean;
    procedure SaveToFile(const pmcFileName: TFileName);
  end;

function TDBSettings.LoadFromFile(const pmcFileName: TFileName): Boolean;
begin
  Result := RecordLoadJson(Self, StringFromFile(pmcFileName), TypeInfo(TDBSettings));
end;

procedure TDBSettings.SaveToFile(const pmcFileName: TFileName);
begin
  FileFromString(RecordSaveJson(Self, TypeInfo(TDBSettings)), pmcFileName);
end;

initialization
  Rtti.ByTypeInfo[TypeInfo(TDBSettingDataBase)].Props.NameChanges(
    ['DBServerName', 'DBName', 'DBUser', 'DBUserPw', 'DBProvider'],
    ['DB-ServerName', 'DB-Name', 'DB-User', 'DB-User-Pw', 'DB-Provider']);
Anwendung so:
Delphi-Quellcode:
var
  rec: TDBSettings;
begin
  rec.LoadFromFile('settings.json');
  ShowMessage(rec.DataBase.DBServerName);

  rec.DataBase.DBServerName := 'TestServer';
  rec.SaveToFile('settings.json');
Bis bald...
Thomas

Uwe Raabe 19. Sep 2023 20:43

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Diesen Ansatz mit dem JSON-Class-Mapping kann man auch ohne externe Bibliothek nur mit Delphi-Bordmitteln realisieren:
Delphi-Quellcode:

interface

uses
  System.Types,
  REST.Json.Types; // wegen des Attributs

type
  TDBSettings = class
  type
    TDataBase = class
    private
      [JSONName('DB-Name')]
      FDBName: String;
      [JSONName('DB-Provider')]
      FDBProvider: Integer;
      FDBServerName: String;
      [JSONName('DB-User')]
      FDBUser: String;
      [JSONName('DB-User-Pw')]
      FDBUserPw: Integer;
    public
      property DBName: String read FDBName write FDBName;
      property DBProvider: Integer read FDBProvider write FDBProvider;
      property DBServerName: String read FDBServerName write FDBServerName;
      property DBUser: String read FDBUser write FDBUser;
      property DBUserPw: Integer read FDBUserPw write FDBUserPw;
    end;
  private
    FDatabase: TDataBase;
  public
    ShorthandSymbol: TStringDynArray;
    constructor Create;
    destructor Destroy; override;
    class function LoadFromFile(const pmcFileName: string): TDBSettings;
    procedure SaveToFile(const pmcFileName: string);
    property Database: TDataBase read FDatabase;
  end;

implementation

constructor TDBSettings.Create;
begin
  inherited Create;
  FDatabase := TDataBase.Create();
end;

destructor TDBSettings.Destroy;
begin
  FDatabase.Free;
  inherited Destroy;
end;
In der Anwendung sieht das dann in etwa so aus:
Delphi-Quellcode:
uses
  System.IOUtils, System.SysUtils,
  REST.Json;

const
  cFileName = 'c:\temp\settings.json';

begin
  var settings := TJson.JsonToObject<TDBSettings>(string.Join(sLineBreak, cArr));
  try
    TFile.WriteAllText(cFileName, TJson.ObjectToJsonString(settings));
  finally
    settings.Free;
  end;

begin
  var settings := TJson.JsonToObject<TDBSettings>(TFile.ReadAllText(cFileName));
  try
    ...
  finally
    settings.Free;
  end;
end;
Nebenbei: Das StringArray als Feld anstatt Property ist zumindest zu hinterfragen.

juergen 19. Sep 2023 21:48

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
@mytbo,
Clonen des Repos.
Pfade in den Bibliothekspfaden hinzugefügt.
Ich hatte ja schon meine Unit mit 8 Klassen. Diese Unit mit deinen Vorschlägen für den Custom-Serializer ergänzt (erst einmal nur für 2 Klassen).
Was soll ich sagen. Es funktioniert! :thumb: Ich bin begeistert. Und das war jetzt auch schnell umgesetzt. Natürlich verstehe ich mORMot nicht, in der Kürze der Zeit. Aber ich habe erst mal ein Ergebnis und das motiviert ja für die Zukunft.

Was mir bei diesem Ansatz gefällt (ich sag's mal als Laie), dass ich eine Unit habe mit den Klassen und der Architektur.
In meiner Hauptunit verwende ich dann global jeweils für jede Klasse eine neue Klassen-Instanz.
Und in der Haupt-Unit gefällt mir nun die Handhabung sehr. Zumindest ist es für mich viel leichter anwendbar und lesbarer.

Vielen Dank also für deinen unermüdlichen Einsatz!!

@Uwe,
auch dir großen Dank für deinen unermüdlichen Einsatz. Ich hatte vor deinem Post schon das Konzept von mytbo angefangen.


Gute N8! :dp:

juergen 19. Sep 2023 22:28

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Eine Frage noch:
Symbolisch sieht meine serialize-Klasse etwa so aus:
Delphi-Quellcode:
  TDBSettings = CLASS( TSynAutoCreateFields )
  PRIVATE
    FShorthandSymbol: ARRAY [1 .. 80] OF STRING;

    PROCEDURE SetShorthandSymbol( CONST AIdx: Integer; CONST Value: STRING );
    FUNCTION GetShorthandSymbol( CONST AIdx: Integer ): STRING;

  PUBLIC
    CONSTRUCTOR Create; // <=== hier erscheint Warnung: W1010 Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TSynAutoCreateFields'
    DESTRUCTOR Destroy; OVERRIDE;

    FUNCTION LoadFromFile( CONST pmcFileName: TFileName ): Boolean;
    PROCEDURE SaveToFile( CONST pmcFileName: TFileName );

    PROPERTY ShorthandSymbol[CONST AIdx: Integer]: STRING READ GetShorthandSymbol WRITE SetShorthandSymbol;
  END;
Bekomme ich hier die Warnmeldung noch weg?

DeddyH 20. Sep 2023 06:10

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Entweder mit override oder, falls das nicht geht, mit reintroduce.

juergen 20. Sep 2023 07:33

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
@DeddyH,
vielen Dank. :thumb:
Ich habe jetzt reintroduce verwendet.
Dazu habe ich noch folgende hilfreiche Seite gefunden => Methoden

juergen 20. Sep 2023 11:16

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Nächstes Problem:
Ich kann in einer anderen Unit nicht auf das Property "ShorthandSymbol" zugreifen, wohl weil die Klasse von "TSynAutoCreateFields" abgeleitet ist.
Delphi-Quellcode:
  TDBSettings = CLASS( TSynAutoCreateFields )
  PRIVATE
    FShorthandSymbol: ARRAY [1 .. 80] OF STRING;

    PROCEDURE SetShorthandSymbol( CONST AIdx: Integer; CONST Value: STRING );
    FUNCTION GetShorthandSymbol( CONST AIdx: Integer ): STRING;

  PUBLIC
    CONSTRUCTOR Create; REINTRODUCE;
    DESTRUCTOR Destroy; OVERRIDE;

    FUNCTION LoadFromFile( CONST pmcFileName: TFileName ): Boolean;
    PROCEDURE SaveToFile( CONST pmcFileName: TFileName );

    PROPERTY ShorthandSymbol[CONST AIdx: Integer]: STRING READ GetShorthandSymbol WRITE SetShorthandSymbol;
  END;
Wie bekommt man das denn noch hin?

Uwe Raabe 20. Sep 2023 11:29

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Zitat:

Zitat von juergen (Beitrag 1527134)
Ich kann in einer anderen Unit nicht auf das Property "ShorthandSymbol" zugreifen, wohl weil die Klasse von "TSynAutoCreateFields" abgeleitet ist.

Wie äußert sich das denn genau?

juergen 20. Sep 2023 12:01

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Uwe Raabe (Beitrag 1527135)
Wie äußert sich das denn genau?

Also wenn ich in der Haupt-Unit hinter dem Namen der Klasse "TDBSettings" einen Punkt eingebe, um dann dort "ShorthandSymbol" auszuwählen, ist dies nicht auffindbar. Die Klassen-Unit ist in der Haupt-Unit unter uses eingebunden.
Die Editormeldung lautet demzufolge: E2233 Auf Eigenschaft 'ShorthandSymbol' kann hier nicht zugegriffen werden

jaenicke 20. Sep 2023 12:10

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
Zitat:

Zitat von juergen (Beitrag 1527137)
Also wenn ich in der Haupt-Unit hinter dem Namen der Klasse "TDBSettings" einen Punkt eingebe, um dann dort "ShorthandSymbol" auszuwählen, ist dies nicht auffindbar.

Das ist auch keine Klasseneigenschaft. Du brauchst erst einmal eine Variable vom Typ TDBSettings. Darin sollte die Property dann nutzbar sein.

juergen 20. Sep 2023 12:40

AW: Json Array of String: Wie Array-Items per Schleife auslesen
 
:oops::oops::wall::wall: Natürlich!
Danke, @jaenicke


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:29 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