Einzelnen Beitrag anzeigen

TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Json Serializer und GUID

  Alt 18. Jun 2021, 10: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