Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Rückgabe-Typ anpassen? (https://www.delphipraxis.net/184480-rueckgabe-typ-anpassen.html)

milos 30. Mär 2015 14:45

Rückgabe-Typ anpassen?
 
Hallo,

ich arbeite gerade an einem JSON-Parser.

Dies wäre eigentlich kein Problem, wenn ich nicht verschiedene Datentypen einbauen wollen würde :)

Hier ein kommentiertes Beispiel aus wiki:
Code:
{
  "id": 1, // integer
  "name": "Foo", // string
  "price": 123, // integer
  "tags": [ // array
    "Bar", // array
    "Eek"  // array
  ],
  "stock": {
    "warehouse": 300, // string
    "retail": 20      // string
  }
}
Wie kann ich das am besten Lösen, dass ich alle Werte in der selben Klasse speichern kann, aber einen anderen Typen abspeichere?

Freundliche Grüsse

Sir Rufo 30. Mär 2015 15:25

AW: Rückgabe-Typ anpassen?
 
Ähm, du willst einen JSON-Parser erstellen oder benutzen?

Eigentlich gibt es schon genug JSON-Parser für Delphi ...

Im Übrigen sind die Kommentare nicht ganz korrekt:
Code:
{
  "id": 1, // integer
  "name": "Foo", // string
  "price": 123, // number
  "tags": [ // array
    "Bar", // array-item string
    "Eek" // array-item string
  ],
  "stock": { // object
    "warehouse": 300, // number
    "retail": 20      // number
  }
}
Zwingende Basis-Lektüre ist hier auch http://json.org

himitsu 30. Mär 2015 15:32

AW: Rückgabe-Typ anpassen?
 
Zitat:

Code:
  "stock": { // object
    "warehouse": 300, // integer
    "retail": 20      // integer
  }

:zwinker:

[add]
Zitat:

Zitat von Sir Rufo (Beitrag 1295345)
Zwingende Basis-Lektüre ist hier auch http://json.org

Ich glaub diese Lektüre haben zu viele JSON-Parser-Entwickler (außerhalb der Welt von JavaScript und kompatiblen dynamischen Sprachen) zu wörtlich genommen.
True und False als einzelne Objekte :wall:
[/add]

Zitat:

Ähm, du willst einen JSON-Parser erstellen oder benutzen?
Gute Frage ... wenn wirklch "erstellen", dann:



Variant und TValue können mehrere "Typen" speichern

oder du reagierst auf den Typen und speicherst dann den "Wert" in einer entsprechenden Variable, ähnlich einem varianten Array.
> String, Double, Array/Objekt, ...

Aber warum schaust du nicht einfach bei anderen Delphi-JSON-Libs ab?
z.B. http://sourceforge.net/projects/lkjson/

(ich weiß jetzt nicht, welche Lib Delphi integriert hat)






Warum baut eigentlich jeder einen JSON-Parser? :stupid:



*hust*
http://svn.geheimniswelten.de:8080/svn/JSON/ (nicht fertig)
Ich hab mich entschieden die "Werte" als String zu speichern. (im Prinzip ist es im JSON dann eh alles ein String)


Wobei ich keinen JSON-Parser bastel (OK, der TJSONReader ist eine Art SAXParser), sondern ein echtes DOM, welches vollständig bearbeitbar ist.
Das Projekt war aber eigentlich entstanden, um die Speicherverwaltung im Multiplattform an einem praktischen Beispiel zu testen.
Siehe die vielen Tickets ala http://www.delphipraxis.net/184063-m...interface.html

Es gibt dazu dbald auch mal einen Artikel über JSON, JavaScript, Delphi, wie das drüben ist, also im JavaScript und vielleicht auch anderswo, und warum das JavaScript-Objekt-Model-Prinzip (nur serialisieren und deserialisieren) nicht unbedingt mit Delphi kompatibel ist.

Sir Rufo 30. Mär 2015 15:43

AW: Rückgabe-Typ anpassen?
 
Warum sollte da was mit Delphi nicht kompatibel sein? :gruebel:

Wenn dem so ist, dann ist Delphi auch nicht mit XML bis hin zu einem Binär-Daten-Format kompatibel (ja, serialisieren).

himitsu 30. Mär 2015 15:57

AW: Rückgabe-Typ anpassen?
 
Weil Delphi keine dynamischen Typen erlaubt?
'ne Variable, wo wirklich alles rein passt und das beliebig verschachtelbar ... das kennt Pascal nativ erstmal nicht.

Ein Variant kann zwar einfache Typen und Arrays enthalten und Objekte, aber nativ keine benamten Objekte, außer man klemmt z.B. ein Dictionary dazwschen.

In JavaScript kann man JSON einfach so deserialisieren und JS baut dann dynmaisch ein Objekt auf, Anhand der gefundenen "Property"-Namen.
Dort ist es eben möglich zur Laufzeit das Objekt umzubauen, was in einer statischen Sprache, wie z.B. Delphi, einfach nicht möglich ist, außer man baut sich dort entsprechende Wrapper-Klassen, so wie sie in fast jedem JSON-Parser enthalten sind.

Entweder man muß der JSON-Deserialisierung geziehlt ein Objkekt/Klasse geben, was schon vorher die nötige Struktur besitzt, oder man baut eben Datenhaltungsklassen in den Baum (ala TJsonObject oder TJsonValuse, wie sie meistens heißen).

Sir Rufo 30. Mär 2015 16:00

AW: Rückgabe-Typ anpassen?
 
Ja, das gleiche Problem hat man mit XML, CSV, Ini-Dateien, ... Binär-Daten.

Aber Java scheint ja eine Zaubersprache zu sein. Da kann man einfach so irgendein JSON rüberschicken und schwupps kann die Anwendung sofort das korrekt interpretieren?

Oder muss selbst bei Java der Kontext klar sein? Was bringt mir die dynamische Instanz, wenn die Anwendung die Eigenschaft "wuppdi" nicht kennt und somit nicht verarbeiten kann? Ich würde mal sagen: Nichts.

Denn die statische Klasse ohne
Delphi-Quellcode:
wuppdi
Eigenschaft ist genauso gut/schlecht wie die dynamische Klasse mit der Eigenschaft, die aber von der Anwendung nicht berücksichtigt wird.

himitsu 30. Mär 2015 16:14

AW: Rückgabe-Typ anpassen?
 
Nein, CSV und XML sind per se "nur" ein Datenformat und in diesem Format ist keine Implementierung vorgegeben, wie man das im Programm verarbeiten soll.

JSON ist eine Serialisierung von JavaScript-Klassen/Variablen und Delphi kann mit derartigen dynamischen Objektstrukturen nix anfangen.
Daß man dieses jetzt für einen systemübergreifenden Datentransfer und somit als "Datenformat" benutzt, ist eine andere Sache.

Die meisten Delphi-Implementationen sind "nur" zum Auslesen oder Erstellen, bzw. Serialaisieren/Deserialisieren gedacht, aber zum direkten Verarbeiten sind Viele einfach ungeeignet/umständlich.



Genau darum bastel ich mir auch ein JSON-DOM, ähnlich den bekannten XML-DOMs, um das einfacher benutzten und vorallem "editieren" zu können, ohne daß man das JSON erst in ein externes "unbekanntes" Delphi-Objekt deserialisieren muß, bzw. ohne das neu aufbauen/kopieren zu müssen.

Fast alle JSON-ReaderImplementationen für Delphi sind quasi read-only oder write-only und es ist, vorallem bei den delphieigenen JSON-Klassen (siehe DBX/DataSnap), recht umständlich aus einem True ein False zu machen.
Ist ja so, als wenn man XML als String mit Pos und Copy versucht zu verarbeiten. :stupid:

milos 3. Apr 2015 00:14

AW: Rückgabe-Typ anpassen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Habe nun einen kleinen Parser gebastelt der ohne Variant Booleans, Integers und Strings zurückgeben kann.
Leider habe ich keine Ahnung wie ich damit nochmal ein ganzes Objekt oder Variable zurückgeben kann.

Ich habe vor die Klasse zum lesen und schreiben benutzen zu können.
Ist mein Ansatz brauchbar?
Source Code: (Projektdaten auch hochgeladen)
Delphi-Quellcode:
unit JsonObject;

interface

uses
  System.SysUtils, Generics.Collections;

type
  TJsonType = ( jntBoolean,
                jntInteger,
                jntString );

  TJsonValue = class
  private
    FValue : string;
    function FGetType : TJsonType;
  public
    procedure GetBoolean(var AOutput : boolean);
    procedure GetInteger(var AOutput : integer);
    procedure GetString(var AOutput : string);

    procedure Assign(AValue : string);
    property ValueType : TJsonType read FGetType;
  end;

  TJsonNodes = class
  protected
  public
    Values : TDictionary<string, TJsonValue>;
    constructor Create();
  end;

  TJsonParser = class(TJsonNodes)
  private
  protected
    FJson : string;
  public
    constructor Create();
    procedure Assign(AJson : string);
    procedure Parse(AJson : string); overload;
    procedure Clear();
    procedure Parse(); overload;
    property Json : string read FJson;
  end;

implementation

{ TJsonParser }

procedure TJsonParser.Assign(AJson: string);
begin
  FJson := AJson;
end;

procedure TJsonParser.Parse(AJson: string);
begin
  FJson := AJson;
  Parse();
end;

procedure TJsonParser.Clear;
var
  CurrentCharIndex: Integer;
  CurrentChar : char;
  OutputString : string;
  InString : boolean;
begin
  InString := False;

  for CurrentCharIndex := 1 to Length(FJson) do
  begin
    CurrentChar := FJson[CurrentCharIndex];

    if (CurrentChar = '"') then
      InString := not InString;

    if ((CurrentChar = ' ') and (InString = false)) or
       ((CurrentChar = #10) or (CurrentChar = #13)) then
      Continue;

    OutputString := OutputString + CurrentChar;
  end;
  FJson := OutputString;
end;

constructor TJsonParser.Create;
begin
  inherited Create;

end;

procedure TJsonParser.Parse;
var
  CurrentCharIndex: Integer;
  CurrentChar : char;

  LineStarted : boolean;

  IndexDone : boolean;
  InIndex : boolean;

  InValue : boolean;
  ValueDone : Boolean;

  InString : boolean;

  LJsonValue : TJsonValue;

  StringBuffer : string;

  LastAdded : string;
begin
  Clear;

  LineStarted := false;

  IndexDone := false;
  InIndex := false;

  InValue := false;
  ValueDone := false;

  InString := False;

  for CurrentCharIndex := 1 to Length(FJson) do
  begin
    CurrentChar := FJson[CurrentCharIndex];


   if (CurrentChar = ',') or (CurrentChar = '}') then
    begin
      if InString then
        raise Exception.Create('String should be ended')
      else if InIndex then
        raise Exception.Create('Index should be ended')
      else if IndexDone and not ValueDone then
        raise Exception.Create('Value should be setted')
      else
      begin
        LineStarted := false;

        IndexDone := false;
        InIndex := false;

        InValue := false;
        ValueDone := false;

        InString := False;


            Values[LastAdded].Assign(StringBuffer);
            StringBuffer := '';
            InValue := false;

        StringBuffer := '';
      end;
    end;


    if not LineStarted then
    begin
      if CurrentChar = '"' then
      begin
        InIndex := true;
        LineStarted := true;

        LJsonValue := TJsonValue.Create;

        Continue;
      end;
    end;

    if LineStarted then
    begin
      if (InIndex) and (not InValue) then
      begin
        if not(CurrentChar = '"') then
        begin
          StringBuffer := StringBuffer + CurrentChar;
          Continue;
        end
        else
        begin
          InIndex := false;
          Values.Add(StringBuffer, LJsonValue);
          LastAdded := StringBuffer;
          StringBuffer := '';
          Continue;
        end;
      end
      else if (not InIndex) and (not InValue) then
      begin
        if CurrentChar = ':' then
        begin
          InValue := true;
          Continue;
        end;
      end
      else if (not InIndex) and (InValue) then
      begin
        StringBuffer := StringBuffer + CurrentChar;
      end;

    end;


  end;
end;

{ TJsonValue }

procedure TJsonValue.Assign(AValue: string);
begin
  FValue := AValue;
end;

function TJsonValue.FGetType: TJsonType;
begin
  if (FValue = 'true') or
     (FValue = 'false') then
     Result := jntBoolean;

end;

procedure TJsonValue.GetBoolean(var AOutput: boolean);
begin
  if (FValue = 'true') then
    AOutput := True
  else if (FValue = 'false') then
    AOutput := false
  else
    raise Exception.Create(FValue + ' is no Boolean');
end;

procedure TJsonValue.GetInteger(var AOutput: integer);
begin
  AOutput := StrToInt(FValue);
end;

procedure TJsonValue.GetString(var AOutput: string);
begin
  if (FValue[1] = '"') and
     (FValue[Length(FValue)] = '"') then
     AOutput := Copy(FValue, 2, Length(FValue)-2);
end;

{ TJsonNodes }

constructor TJsonNodes.Create;
begin
  Values := TDictionary<string, TJsonValue>.Create();
end;

end.
Delphi-Quellcode:
...

    Json : TJsonParser;

...

procedure TForm1.btn1Click(Sender: TObject);
var
  LValueID : integer;
  LValueAge : integer;
  LValueName : string;
  LValueURL : string;
  LValueValid : Boolean;
begin
  Json := TJsonParser.Create;
  Json.Assign(mmo1.Text);
  Json.Parse;
  mmo1.Text := Json.Json;


  Json.Values['id'].GetInteger(LValueID);
  Json.Values['age'].GetInteger(LValueAge);

  Json.Values['name'].GetString(LValueName);
  Json.Values['url'].GetString(LValueURL);

  Json.Values['valid'].GetBoolean(LValueValid);

  Showmessage(IntToStr(LValueID));
  Showmessage(IntToStr(LValueAge));
  Showmessage(LValueName);
  Showmessage(LValueURL);

  if LValueValid then
    Showmessage('Valid')
  else
    Showmessage('Invalid');

end;
Json code der so funktioniert:
Code:
{
  "id": 1,
  "age": 18,
  "name": "Milos",
  "url": "http://delphipraxis.net",
  "valid": true
}
Was haltet ihr von dem ansatz?
Was würdet ihr anders machen?

MfG

himitsu 3. Apr 2015 07:07

AW: Rückgabe-Typ anpassen?
 
Dein Zurückgeben geht und man kann es auch so machen,
wobei es natrülich etwas doppelt gemoppelt ist, da der Compiler anhand der Signatur (die Typen der Parameter, aber nicht des Results) auch automatisch unterscheiden kann. (overload)
Delphi-Quellcode:
procedure GetValue(var AOutput : boolean); overload;
procedure GetValue(var AOutput : integer); overload;
procedure GetValue(var AOutput : string); overload;
Delphi-Quellcode:
function GetBoolean: boolean;
function GetInteger: integer;
function GetString: string;
Zitat:

Zitat von milos (Beitrag 1296039)
Leider habe ich keine Ahnung wie ich damit nochmal ein ganzes Objekt oder Variable zurückgeben kann.

Was ist Variable?

Delphi-Quellcode:
procedure GetValue(AOutput TMyObject); overload;

Hier kann man bei einem besehendem Objekt die Parameter auslesen, man kann auch Parameter zuwisen/ändern und natürlich Methoden aufrufen,
aber die Instanz muß extern erstellt und reingegeben werden.
Die Instanz wird natürlich auch extern freigegeben, am Besten in der selben Ebene, wie das Erstellen.
Delphi-Quellcode:
procedure GetValue(var AOutput TMyObject); overload;

Wie vorherriges, außer daß man z.B. bei
Delphi-Quellcode:
if noch Assigned(AOutput) then
intern eine neue Instanz erstellen kann.
Von extern kommt also entweder ein Objekt oder nil rein. Und man kann intern das externe Objekt auch freigeben (FreeAndNil) und/oder ein Neues erstellen.
Delphi-Quellcode:
function GetOject: TMyObject;

Hier wird das Objekt ausschließlich intern erstellt.
Freigeben muß es dann entweder der Aufrufer und ruft am Ende Free auf. (siehe Delphi-Referenz durchsuchenGetMemory)
Oder der Ersteller ist Owner, verwaltet es intern und gibt es am Ende selbst frei, z.B. wenn er selber freigegeben wird. (siehe Memo.Lines oder Memo.Font).

Popov 3. Apr 2015 10:03

AW: Rückgabe-Typ anpassen?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1295345)
Ähm, du willst einen JSON-Parser erstellen oder benutzen?

Eigentlich gibt es schon genug JSON-Parser für Delphi ...

Mal etwas OT. Ist die Aussage richtig? Als ich das letzte Mal auf die Idee kam mal JSON auszuprobieren, hat nichts, aber auch wirklich nichts mit Delphi 7 problemlos funktioniert. Irgendwann gab ich es auf. Gibt es inzwischen so große Auswahl, dass ich es erneut versuchen kann und der TE drauf verzichten kann etwas neues zu schreiben?


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:49 Uhr.
Seite 1 von 3  1 23      

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