![]() |
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:
Wie kann ich das am besten Lösen, dass ich alle Werte in der selben Klasse speichern kann, aber einen anderen Typen abspeichere?
{
"id": 1, // integer "name": "Foo", // string "price": 123, // integer "tags": [ // array "Bar", // array "Eek" // array ], "stock": { "warehouse": 300, // string "retail": 20 // string } } Freundliche Grüsse |
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:
Zwingende Basis-Lektüre ist hier auch
{
"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 } } ![]() |
AW: Rückgabe-Typ anpassen?
Zitat:
[add] Zitat:
True und False als einzelne Objekte :wall: [/add] Zitat:
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. ![]() (ich weiß jetzt nicht, welche Lib Delphi integriert hat) Warum baut eigentlich jeder einen JSON-Parser? :stupid: *hust* ![]() 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 ![]() 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. |
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). |
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). |
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:
Eigenschaft ist genauso gut/schlecht wie die dynamische Klasse mit der Eigenschaft, die aber von der Anwendung nicht berücksichtigt wird.
wuppdi
|
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: |
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 code der so funktioniert:
...
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;
Code:
Was haltet ihr von dem ansatz?
{
"id": 1, "age": 18, "name": "Milos", "url": "http://delphipraxis.net", "valid": true } Was würdet ihr anders machen? MfG |
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:
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:
intern eine neue Instanz erstellen kann.
if noch Assigned(AOutput) then
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 ![]() 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). |
AW: Rückgabe-Typ anpassen?
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:42 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