AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Json Serializer und GUID

Ein Thema von backdraft · begonnen am 18. Jun 2021 · letzter Beitrag vom 18. Jun 2021
Antwort Antwort
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
291 Beiträge
 
Delphi 10.4 Sydney
 
#1

Json Serializer und GUID

  Alt 18. Jun 2021, 08:49
Hallo zusammen,

ich nutze mittlerweile den Delphi Json Serialzter in vielen Projekten.
Jetzt bin ich das erste mal an eine GUID geraten, und da funktioniert er scheinbar nicht.

Konvertiere ich ein GUID nach JSon und wieder zurück ist der hintere Teil abgeschnitten.
Mache ich was falsch, oder ist das ein Bug?

Code:
  var lText := TStringList.Create;
  try
    var lGuid := TGUID.NewGuid;
    lText.Add(lGuid.ToString);

    var lJson: string;
    var lSerializer := TJsonSerializer.Create;
    try
      lJson := lSerializer.Serialize(lGuid);
    finally
      FreeAndNil(lSerializer);
    end;

    lText.Add(lJson);

    var lNewGuid: TGUID;
    var lDeserializer := TJsonSerializer.Create;
    try
      lNewGuid := lDeserializer.Deserialize<TGUID>(lJson);
    finally
      FreeAndNil(lSerializer);
    end;

    lText.Add(lNewGuid.ToString);

    ShowMessage(lText.Text);
  finally
    FreeAndNil(lText);
  end;
Ergebnis:
Code:
{1BBCB1AA-1BF2-42F7-96E2-2A84ED6699BA}
{"D1":465351082,"D2":7154,"D3":17143}
{1BBCB1AA-1BF2-42F7-0000-000000000000}
Es scheint die Variable D4 zu fehlen von TGUID (array[0..7] of byte).
Weiss jemand ob man dem Parser das manuell beibringen kann?

Grüße
Oliver
Oliver
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
8.093 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 11:28
Leider ist die Dokumentation recht dürftig...
Einen eigenen Converter bekommst du soweit ich mich erinnere so rein:
Delphi-Quellcode:
TJsonD4Converter = class(TJsonConverter)
...

  TGuid = record
    [JsonConverter(TJsonD4Converter)]
    D4: array[0..7] of Byte;
Allerdings brauchst du dann noch einen passenden Provider, da das über die Rtti mit dem Array nicht klappt soweit ich mich erinnere...

Ich habe leider aktuell keine Zeit um nachzuschauen.

Vielleicht bekommst du das auch ohne Attribut (und damit einer Kopie der Recorddefinition) hin, aber das habe ich noch nicht versucht.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
3.834 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#3

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 11:44
Bekanntes Problem. Für das D4 Feld gibt es keine typeinfo. Sollte irgendwo im QP schon reportet sein.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
2.810 Beiträge
 
Delphi 10.2 Tokyo Enterprise
 
#4

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 11:55
Es liegt, wie jaenicke andeutet, an der Typ-Definition für D4.
Das array[0..7] of Byte bekommt die Rtti nicht als Typ aufgedröselt.
Vielleicht ein Bug, weil anscheinend die Rtti immer einen richtigen Alias-Namen braucht.
Beim Erstellen des notwendigen TRttiField für D4, wird der FFieldType nicht ordentlich aufgelöst.
Die PTypeInfo dafür müsste als TTypeKind.tkArray rauskommen.
Delphi-Quellcode:
constructor TRttiField.Create(APackage: TRttiPackage; AParent: TRttiObject;
  var P: PByte);
begin
  inherited Create(APackage, AParent, P);
  FFieldType := GetFieldType; // hier kommt nil zurück für statische Arrays.
  FOffset := GetOffset;
end;
...
function TRttiRecordField.GetFieldType: TRttiType;
begin
  Result := Pool.TypeOrNil(Handle^.Field.TypeRef); //TypeRef ist hier nil, daher kommt von TypeOrNil auch nichts zurück. Das passiert schon beim Erzeugen des Basis-Typs TRttiObject im constructor
end;
Darum ist in TJsonSerializerWriter.WriteProperty in Zeile 1361 AProperty.Contract := ResolveContract(AProperty.TypeInf); der Wert für AProperty.TypeInf nil und es kommt kein vernünftiger Contract: TJsonContract bei raus.

Du kannst dich zumindest dahingehend behelfen, indem du dir einen Hilftyp zusammen schusterst und später dann immer zwischen deinen GUID-Typ und den richtigen TGUID-Typ aus der RTL castest:

Konsolenbeispiel:
Delphi-Quellcode:
program Project6;

{$APPTYPE CONSOLE}
{$R *.res}

uses
    System.SysUtils,
    System.Classes,
    System.JSON,
    System.JSON.Serializers;

type
    // TMyGUID = TGUID;
    TMyArray = array [0 .. 7] of Byte;

    TMyGUID = record
    public
        D1: Cardinal;
        D2: Word;
        D3: Word;
        D4: TMyArray;
        class function NewGuid: TMyGUID; static;
        function ToString: string;
    end;

class function TMyGUID.NewGuid: TMyGUID;
var
    foo: TGUID;
begin
    foo := TGuid.NewGuid;
    Result.D1 := foo.D1;
    Result.D2 := foo.D2;
    Result.D3 := foo.D3;
    Result.D4 := TMyArray(foo.D4);
end;

function TMyGUID.ToString: string;
begin
    Result := TGuid(Self).Tostring;
end;

var
    lGuid, lNewGuid: TMyGUID;
    lText: TStringList;
    lJson: string;
    lSerializer, lDeserializer: TJsonSerializer;

begin
    try
        lText := TStringList.Create;
        try
            lGuid := TMyGUID.NewGuid;
            lText.Add(lGuid.ToString);

            lSerializer := TJsonSerializer.Create;
            try
                lJson := lSerializer.Serialize<TMyGUID>(lGuid);
            finally
                FreeAndNil(lSerializer);
            end;

            lText.Add(lJson);

            lDeserializer := TJsonSerializer.Create;
            try
                lNewGuid := lDeserializer.Deserialize<TMyGUID>(lJson);
            finally
                FreeAndNil(lSerializer);
            end;

            lText.Add(lNewGuid.ToString);

            Writeln(lText.Text);
        finally
            FreeAndNil(lText);
        end;
    except
        on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
    end;
    Readln;
end.
Über den Call Stack TJsonDefaultContractResolver.ResolveContract | TJsonDefaultContractResolver.CreateContract | TJsonDefaultContractResolver.CreateArrayContract wird eine Instanz von TJsonArrayContract erzeugt, mit der die Serialisierung arbeiten kann.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
3.834 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#5

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 14:59
Einfach nen Konverter für TGUID schreiben und am Serializer hinzufügen, fertig:

Delphi-Quellcode:
type
  TGUIDConverter = class(TJsonConverter)
  public
    function CanConvert(ATypeInfo: PTypeInfo): Boolean; override;
    function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue;
      const ASerializer: TJsonSerializer): TValue; override;
    procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue;
      const ASerializer: TJsonSerializer);override;
  end;

function TGUIDConverter.CanConvert(ATypeInfo: PTypeInfo): Boolean;
begin
  Result := ATypeInfo = System.TypeInfo(TGUID);
end;

function TGUIDConverter.ReadJson(const AReader: TJsonReader;
  ATypeInf: PTypeInfo; const AExistingValue: TValue;
  const ASerializer: TJsonSerializer): TValue;
begin
  Result := TValue.From(TGuid.Create(AReader.Value.AsString));
end;

procedure TGUIDConverter.WriteJson(const AWriter: TJsonWriter;
  const AValue: TValue; const ASerializer: TJsonSerializer);
begin
  AWriter.WriteValue(AValue.AsType<TGUID>);
end;

type
  TFooBar = class
    GUID: TGUID;
  end;
procedure Main;
begin
  var foobar := TFooBar.Create;
  foobar.GUID := TGUID.NewGuid;
  Writeln(foobar.GUID.ToString);

  var lSerializer := TJsonSerializer.Create;
  lSerializer.Converters.Add(TGUIDConverter.Create);

  var lJson := lSerializer.Serialize(foobar);

  Writeln(lJson);

  var newFooBar := lSerializer.Deserialize<TFooBar>(lJson);
  Writeln(newFooBar.GUID.ToString);
end;
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
2.810 Beiträge
 
Delphi 10.2 Tokyo Enterprise
 
#6

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 15:40

Leider kein :huld: Smiley!


@backdraft: Aber dann den eigenen Konverter noch freigeben, dass fehlt in Stevies Beispiel.
  Mit Zitat antworten Zitat
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
291 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 15:52
Ja vielen Dank.
Klappt perfekt.
Auf die Idee, dass ein einfach eine Converterproperty gibt bin ich nicht gekommen.
Ich dachte man kann es irgendwie über der Variable definieren mit [].
Läuft bestens.
Oliver
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +2. Es ist jetzt 06:21 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf