![]() |
TGUID-Wert innerhalb eines records in Json konvertieren (XE7)
Hallo, Ich habe ein Problem beim konvertieren eines TGUID-Werts in einen JSON-string. Zunächst die Variante, die funktioniert:
Delphi-Quellcode:
Ich gebe also einen Interceptor an, der auch vom Marshaller aufgerufen wird, in dem die GUID in einen String umgewandelt wird.
{$METHODINFO ON}
TTest = class (TObject) private [JSonReflect (ctString, rtString, TGuidInterceptor, nil, True)] FID: TGUID; public property ID: TGUID read FID write FID; end; {$METHODINFO OFF} (..) S := TJson.ObjectToJsonString (TestObj); In der Regel habe ich aber die GUID zusammen mit anderen Werten in einem Record, darum sehen die Objekte also bspw. so aus:
Delphi-Quellcode:
In dem Fall wird der Interceptor leider nicht aufgerufen! Es wird versucht, das Record zu konvertieren, darin dann den GUID-Wert, und beim 4. Feld knallt es dann, weil das byte-array nicht konvertiert werden kann.
type
TRec = record [JSonReflect (ctString, rtString, TGuidInterceptor, nil, True)] FID: TGUID; end; TTest = class (TObject) private FRec: TRec; public property Rec: TRec read FRec; end; (..) S := TJson.ObjectToJsonString (TestObj); Hat jemand eine Ahnung, wie man das umschiffen kann? Vielen Dank, Tobias |
AW: TGUID-Wert innerhalb eines records in Json konvertieren (XE7)
Kannst du die uses Klauseln posten?
Compiler Direktiven? Welches Fehlerbild kommt bei dir? Ich erhalte eine Exception: "InsufficientRtti" Unabhängig von welcher Klasse das Objekt ist (Delphi XE5). |
AW: TGUID-Wert innerhalb eines records in Json konvertieren (XE7)
Hallo Chris,
hier die komplette Unit... einfach aus einer neuen Konsolen-dpr aufzurufen ohne spezielle weitere Projekteinstellungen:
Delphi-Quellcode:
Zwei Zeilen, Feld FRec betreffend, sind auskommentiert. D. h. so wie o. a. läuft die Prozedur durch. Nimmt man die beiden Zeilen mit FRec rein, so kommt die RTTI-Exception. Das liegt daran, dass er für das TGUID-Feld im FRec nicht den Interceptor aufruft. Aber wie macht man's richtig? ;-)
>>>>>
unit TestDef; interface uses System.Rtti, System.JSON, System.SysUtils, REST.Json, REST.Json.Types, REST.JsonReflect; type TGuidInterceptor = class (TJSONInterceptor) public function StringConverter (AData: TObject; AField: string): string; override; procedure StringReverter (AData: TObject; AField: string; AArg: string); override; end; type {$METHODINFO ON} TRec = record [JSonReflect (ctString, rtString, TGuidInterceptor, nil, True)] FID: TGUID; end; TTest = class (TObject) public [JSonReflect (ctString, rtString, TGuidInterceptor, nil, False)] FID: TGUID; // FRec: TRec; end; {$METHODINFO OFF} procedure RunTest; implementation function TGuidInterceptor.StringConverter (AData: TObject; AField: string): string; var Ctx: TRttiContext; GUID: TGUID; begin GUID := Ctx.GetType (AData.ClassType).GetField (AField).GetValue (AData).AsType<TGUID>; Result := GUIDToString (GUID); end; procedure TGuidInterceptor.StringReverter (AData: TObject; AField: string; AArg: string); var Ctx: TRttiContext; GUID: TGUID; begin GUID := StringToGUID (AArg); Ctx.GetType (AData.ClassType).GetField (AField).SetValue (AData, TValue.From<TGUID> (GUID)); end; procedure RunTest; var TestObj: TTest; TestRev: TTest; S1, S2: string; LJSONValue: TJSONValue; LJSONObject: TJSONObject; begin TestObj := TTest.Create; try TestObj.FID := TGUID.NewGuid; // TestObj.FRec.FID := TestObj.FID; S1 := TJson.ObjectToJsonString (TestObj); LJSONValue := TJSONObject.ParseJSONValue (S1); Assert (Assigned (LJSONValue) and (LJSONValue is TJSONObject)); if Assigned (LJSONValue) and (LJSONValue is TJSONObject) then begin LJSONObject := LJSONValue as TJSONObject; TestRev := TTest.Create; try TJson.JsonToObject (TestRev, LJSONObject); S2 := TJson.ObjectToJsonString (TestObj); Assert (S1.Equals (S2)); finally TestRev.Free; end; end; finally TestObj.Free; end; end; end. <<<<< Tobias |
AW: TGUID-Wert innerhalb eines records in Json konvertieren (XE7)
Zitat:
Delphi-Quellcode:
Hätte man statt
TGUID = packed record
D1: LongWord; D2: Word; D3: Word; D4: array[0..7] of Byte; class operator Equal(const Left, Right: TGUID): Boolean; class operator NotEqual(const Left, Right: TGUID): Boolean; class function Empty: TGUID; static; end;
Delphi-Quellcode:
gesagt
D4: array[0..7] of Byte;
Delphi-Quellcode:
wäre alles glatt gelaufen.
D4: TMyArray
Hier ein ganz kurzer Test, ganz ohne TGUID:
Delphi-Quellcode:
Wie man das im Fall von
unit Unit16;
//{$DEFINE DOFAIL} interface type {$IFDEF DOFAIL} TSomeRecord = record myStaticArray: array[0..1] of Byte; end; {$ELSE} TSomeRecord = record public type TMyByteArray = array[0..1] of Byte; public var myStaticArray: TMyByteArray; end; {$ENDIF} TMyClass = class(TObject) public var myRecord: TSomeRecord; end; procedure testMarshalling(); implementation uses REST.Json; procedure testMarshalling(); var instance: TMyClass; asJson: String; begin instance := TMyClass.Create(); try asJson := TJson.ObjectToJsonString(instance); finally instance.Destroy(); end; instance := TJson.JsonToObject<TMyClass>(asJson); end; end.
Delphi-Quellcode:
umschiffen kann? Ich habe ehrlich gesagt auch keine Ahnung. Die Serialisierung von Records läuft eh komisch ab, das siehst du ja auch daran dass er sie einfach als Json-Array serialisiert. Ich glaube die RTTI weiß zur Laufzeit auch gar nicht mehr, wie die Record-Felder hießen.
System.TGUID
|
AW: TGUID-Wert innerhalb eines records in Json konvertieren (XE7)
Wenn ich das richtig in Erinnerung habe, funktioniert das Interceptor-Prinzip nur für Felder einer Klasse. Das sieht man schon daran, daß man als Parameter eben ein TObject und den Feldnamen bekommt. Wie sollte hier die Record-Instanz im Parameter AData denn untergebracht werden?
Als Lösung fällt mir hier nur ein, für den Record FRec einen eigenen Interceptor zu registrieren, der die Umwandlung des kompletten Record übernimmt. |
AW: TGUID-Wert innerhalb eines records in Json konvertieren (XE7)
Der korrekte und saubere Weg führt über ein DTO (DataTransferObject). Nur da definiert man dann die Attribute und serialisiert.
Für die Umwandlung vom eigentlichen Typen zum DTO (und auch zurück) erstellt man sich eine Assembler-Klasse als Bindeglied. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:18 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