![]() |
JSON richtig parsen
Hi,
ich versucht seit Stunden ein JSON-String zu parsen, aber ich raff's nicht. Ich könnte mal ein bisschen Schützenhilfe von euch gebrauchen ;-) Mein JSON-String sieht so aus:
Code:
Ziel ist es alle Elemente des Array in Objekte zu überführen.
[{"Lfdnr":"1","Fehlernummer":"1","Programmversion":"4.6.1.123","DBVersion":"15","Status":"1","Kategorie":"Testfehler","Dringlichkeit":"1","NoWork":"0","Kurzbeschreibung":"Das ist ein Testfehler","Langbeschreibung":"Das ist der Langtext von Testfehler","Benutzer":"HC","BenutzerName":"Hobbycoder","Datum":"2017-03-22 00:00:00","BehInVersion":"","BehDatum":"0000-00-00 00:00:00","BehVon":""},{"Lfdnr":"2","Fehlernummer":"1","Programmversion":"4.6.1.123","DBVersion":"15","Status":"1","Kategorie":"Testfehler","Dringlichkeit":"1","NoWork":"0","Kurzbeschreibung":"Das ist ein Testfehler","Langbeschreibung":"Das ist der Langtext von Testfehler","Benutzer":"HC","BenutzerName":"Hobbycoder","Datum":"2017-03-22 00:00:00","BehInVersion":"","BehDatum":"0000-00-00 00:00:00","BehVon":""},{"Lfdnr":"3","Fehlernummer":"2","Programmversion":"4.6.1.155","DBVersion":"200","Status":"2","Kategorie":"Nocheinekategorie","Dringlichkeit":"3","NoWork":"1","Kurzbeschreibung":"kb","Langbeschreibung":"lb","Benutzer":"WT","BenutzerName":"Willi Tester","Datum":"2017-03-22 00:00:00","BehInVersion":"","BehDatum":"0000-00-00 00:00:00","BehVon":""}]
Die Objekte sehen (etwas gekürzt) so aus:
Delphi-Quellcode:
Das Objekt habe ich hier nur mal aufgeführt, damit man sehen kann wie ich das ablegen will.
TErrorMessage=class
property Lfdnr: Integer read FLfdnr write SetLfdnr; property Fehlernummer: string read FFehlernummer write SetFehlernummer; property Programmversion: string read FProgrammversion write SetProgrammversion; property DBVersion: string read FDBVersion write SetDBVersion; property Status: TEMStatus read FStatus write SetStatus; property Kategorie: string read FKategorie write SetKategorie; property Dringlichkeit: TEMDringlichkeit read FDringlichkeit write SetDringlichkeit; property NoWork: Boolean read FNoWork write SetNoWork; property Kurzbeschreibung: string read FKurzbeschreibung write SetKurzbeschreibung; property Langbeschreibung: string read FLangbeschreibung write SetLangbeschreibung; property Benutzer: string read FBenutzer write SetBenutzer; property Benutzername: string read FBenutzername write SetBenutzername; property Datum: TDateTime read FDatum write SetDatum; property BehInVersion: string read FBehInVersion write SetBehInVersion; property BenDatum: TDateTime read FBenDatum write SetBenDatum; property BenVon: string read FBenVon write SetBenVon; end; Mein Source dafür sie so aus:
Delphi-Quellcode:
Nur so, läuft er nicht. Sieht auch ein bisschen verrissen aus, weil ich auf Grund der Beispiele im Internet das eine oder andere ausprobiert habe, aber nicht wirklich zu einem lauffähigen Ergebnis gekommen bin.
procedure TErrorMessageList.GetListFromWeb(path: string);
var http: TIdHTTP; jsonstr: string; st: TStringStream; jsonval: TJSONValue; jsondsz: TJSONValue; em: TErrorMessage; begin http:=TIdHTTP.Create(nil); st:=TStringStream.Create; try self.Clear; http.Get(path+'getlist.php', st); st.Position:=0; jsonstr:=st.ReadString(st.Size); try jsonval:=TJSONObject.ParseJSONValue(jsonstr); // if json is TJSONArray then // begin for jsondsz in (jsonval as TJSONArray) do begin Try em:=TErrorMessage.Create; em.Lfdnr:=StrToInt(TJSONString(jsondsz.GetValue('lfdnr')).Value); em.Fehlernummer:=jsondsz.GetValue('fehlernummer').ToString; em.Programmversion:=jsondsz.GetValue('programmversion').ToString; em.DBVersion:=jsondsz.GetValue('dbversion').ToString; em.Status:=TEMStatus(StrToInt(jsondsz.GetValue('status').ToString)); em.Kategorie:=jsondsz.GetValue('kategorie').ToString; em.Dringlichkeit:=TEMDringlichkeit(StrToInt(jsondsz.GetValue('dringlichkeit').ToString)); em.NoWork:=Int2Bool(StrToInt(jsondsz.GetValue('nowork').ToString)); em.Kurzbeschreibung:=jsondsz.GetValue('kurzbeschreibung').ToString; em.Langbeschreibung:=jsondsz.GetValue('langbeschreibung').ToString; em.Benutzer:=jsondsz.GetValue('benutzer').ToString; em.Benutzername:=jsondsz.GetValue('benutzername').ToString; self.Add(em); except em.Free; End; end; //end; finally end; finally http.Free; st.Free; end; end; Nach Möglichkeit will ich nur System.json verwenden, und keine anderen oder fremde Units. Wäre für eure Hilfe sehr dankbar. Gruß Hobbycoder. |
AW: JSON richtig parsen
Liste der Anhänge anzeigen (Anzahl: 1)
Du machst es dir viel zu kompliziert - Das ist alles schon fertig: Mit TJson.JsonToObject<TErrorMessage>(yourJsonObject) hast du eine automatische Umwandlung deines Json-Objekts in TErrorMessage.
Hier als Beispiel:
Delphi-Quellcode:
// JCL_DEBUG_EXPERT_GENERATEJDBG OFF
// JCL_DEBUG_EXPERT_INSERTJDBG OFF // JCL_DEBUG_EXPERT_DELETEMAPFILE OFF program Project19; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.IoUtils, System.Json, Rest.Json, Unit10 in 'Unit10.pas'; const filePath = 'y:\jsonTest.txt'; procedure parseJson(); var fileContent: String; messagesJson: TJSONArray; messageJson: TJSONValue; messageObject: TErrorMessage; messagesObjects: TArray<TErrorMessage>; begin fileContent := TFile.ReadAllText(filePath); messagesJson := TJSONObject.ParseJSONValue(fileContent) as TJSONArray; messagesObjects := []; for messageJson in messagesJson do begin messageObject := TJson.JsonToObject<TErrorMessage>(messageJson as TJsonObject); messagesObjects := messagesObjects + [messageObject]; end; readln; end; begin parseJson(); end. PS: Ja, die Umwandlung stammt aus Rest.Json.pas die beim RAD-Studio dabei ist. Ich wundere mich auch, was das mit REST zu tun haben soll, warum das nicht auch in System.Json.pas steckt. Aber das ist hoffentlich kein Stolperstrick? |
AW: JSON richtig parsen
Danke ;-) Das funktioniert ja wie geschnitten Brot ;-)
Zu meinem Verständnis: Die Voraussetzungen sind, nehme ich mal an, dass a) der JSON-String NUR ein Array enthält, und b) die Bezeichner für die Values innerhalb des Array den Properties des Objekts entsprechen? Wie findet die Funktion denn die passenden Properties? Und werden die Typen immer passend umgewandelt (String, Int, TDatetime, etc)? |
AW: JSON richtig parsen
Zu a)
Ja - Eigentlich musst du ja im Voraus wissen, was du bekommst. Ob es ein Array mit X Dingen drin ist, oder nur ein Ding. Wenn beides auftreten kann muss dein Code mit beiden Wegen zurechtkommen. Oder meinst du dass der "richtige" Inhalt wiederum ein Objekt mit einer Handvoll Feldern und ein Feld ist dieses Array? Auch das ist kein Problem, man muss es nur abbilden im Code. Zu b) Das Umwandeln des Json in dein TErrorMessage-Objekt fasst die Properties nicht an. Das siehst du wenn du z.B. einen Haltepunkt in deine Setter-Methode setzt. Der "Marshaller" erstellt zuerst ein TErrorMessage-Objekt, dann sucht er sich für jedes JSON-Feld das passende Feld in deinem Delphi-Objekt und steckt den Wert da hinein. Da der JSON-String den du angegeben hast anscheinend einwandfrei war, klappt das auch mit Dingen wie TDateTime (wie man sieht). Eine Besonderheit: In deinem Fall hat das so toll geklappt weil du dieser merkwürdigen Konvention (bzw. oft leider Notwendigkeit) folgst allen Feldnamen immer ein "F" voran zu stellen. In deinem Json-String ist das nicht so. Trotzdem hat das geklappt. Warum das so ist steht hier: ![]() Ich rege mich darüber eher auf, aber im verlinkten Thema steht auch wie du z.B. die Felder in deiner Klasse auch anders nennen kannst als im JSON und der Mechanismus trotzdem weiterhin funktionieren kann. |
AW: JSON richtig parsen
Den verlinkten Artikel habe ich gelesen.
Demnach scheint, auch wenn du keine Freund davon bist, eine einheitliche Namenskonvention zwischen json und objekt für die geringsten Komplikationen zu sorgen. Damit kann ich leben. Mein Objekt umfasst ja mehr Properties, als im Json übermittelt. Du Funktion hat damit keine Probleme. Was aber passiert im umgekehrten Fall? Kommt eine Exception, oder ignoriert die Funktion das dann einfach? Das könnte ja schnell passieren, denn nicht immer hat man selbst Einfluss auf den JSON-String bzw. wann der mal verändert wird. (Ich könnt's auch ausprobieren, aber vielleicht weißt du das auch so.) |
AW: JSON richtig parsen
Und ein Problem hab ich dann doch noch.
Ich habe ja 2 Properties als enumeration type.
Delphi-Quellcode:
Das würde ich auch gerne so belassen.
TEMStatus=(emsNeu, emsIB, emsKorr, emsGepr);
TEMDringlichkeit=(emdLowest, emdLower, emdNormal, emdHigh, emdHighest); property Status: TEMStatus read FStatus write SetStatus; property Dringlichkeit: TEMDringlichkeit read FDringlichkeit write SetDringlichkeit; Diese werden von TJson.JsonToObject<TErrorMessage>(messageJson as TJsonObject) nicht korrect gesetzt. Wahrscheinlich eher gar nicht, da json selbst diese Datentypen nicht kennt. Da ich sie auch nicht im Create vordefiniere, bleiben sie ohne wert und können bei der späteren Verwendung auch nicht korrekt gecastet werden. Wie löst man sowas? Im JSON sind die ja als Integer drin. |
AW: JSON richtig parsen
Zu 1)
Ich habe es nicht im Kopf, aber durch Ausprobieren sieht man meine ich dass er weder auf die Nase fällt wenn dein Objekt mehr Felder hat als das Json, noch umgekehrt wenn das Json mehr Felder hat als dein Objekt. Eigentlich ist dir ja klar was du als Json bekommst. Das ist doch ein perfekter Kandidat für Unit-Tests. Sollte sich der Json-Mechanismus in Delphi eines Tages ändern würdest du das mit deinen Tests ja sofort merken wenn dort etwas nicht mehr stimmt ;-) Zu 2) Enumemerationen sind jetzt etwas kniffelig. Denn in JSON gibt es so etwas nicht. Deine Gegenstelle legt die jetzt als Integer ab sagst du. Hätte man auch als Strings machen können. Ich sehe zwei Möglichkeiten:
Der Interceptor würde die Zahl aus deinem Json auswerten können und wieder auf deinen Enum-Typen zurückführen können. Ich finde ![]() Ich finde es auch in dem Fall einen Overkill. Im Fall Enum/Zahl - ganz ehrlich - Warum nicht einfach die Felder in deinem Objekt auch als Zahl (z.B. Byte) machen und die Getter & Property anpassen? Zu irgend etwas müssen diese Getter ja gut sein ;-)
Delphi-Quellcode:
type
TEMStatus=(emsNeu, emsIB, emsKorr, emsGepr); TEMDringlichkeit=(emdLowest, emdLower, emdNormal, emdHigh, emdHighest); TErrorMessage = class private var FStatus: Byte; FDringlichkeit: Byte; private function getStatus(): TEMStatus; function getDringlicheit: TEMDringlichkeit; public property Status: TEMStatus read getStatus; property Dringlichkeit: TEMDringlichkeit read getDringlicheit; end; implementation { TErrorMessage } function TErrorMessage.getDringlicheit(): TEMDringlichkeit; begin Result := TEMDringlichkeit(FDringlichkeit); end; function TErrorMessage.getStatus(): TEMStatus; begin Result := TEMStatus(FStatus); end; |
AW: JSON richtig parsen
Warum kann man eigentlich Properties nicht überladen? Das wäre doch jetzt echt praktisch für diesen Fall. :-D
|
AW: JSON richtig parsen
Properties kann man zwar nicht überladen, die Getter und Setter aber schon, fals diese in der Basisklasse entsprechend angelegt wurden
|
AW: JSON richtig parsen
Ich habe das mit den enumererationen jetzt so gelöst, in dem ich mir eine Helperklasse angelegt habe, in die der Json geparst wird. Danach überführe ich das in die Zielklasse mit entsprechenden Umsetzungen. Nicht die schönste Lösung, aber für meine Zwecke reichst.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:15 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