mORMot and Open Source friends
Check-in [e9631f6218]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:{1975} fixed nasty issue with JSON serialization of custom records (e.g. TGUID)
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e9631f62183c5eba8047bca4608b24be719309ea
User & Date: ab 2015-10-09 09:29:46
Context
2015-10-09
09:30
{1976} TGUID will now be accepted as SOA argument in all versions of Delphi check-in: 257cf80432 user: ab tags: trunk
09:29
{1975} fixed nasty issue with JSON serialization of custom records (e.g. TGUID) check-in: e9631f6218 user: ab tags: trunk
08:09
{1974} fixed contract recognition error when ResultAsJSONObjectWithoutResult is set check-in: 890f86545b user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SynCommons.pas.

5925
5926
5927
5928
5929
5930
5931

5932
5933
5934
5935
5936
5937
5938
.....
30136
30137
30138
30139
30140
30141
30142
30143
30144
30145
30146
30147
30148
30149
30150
.....
31513
31514
31515
31516
31517
31518
31519
31520

31521
31522
31523
31524
31525
31526
31527
31528


31529
31530
31531
31532
31533
31534
31535
.....
31540
31541
31542
31543
31544
31545
31546
31547
31548
31549
31550
31551
31552
31553
31554
31555
31556
.....
31564
31565
31566
31567
31568
31569
31570
31571

31572
31573
31574
31575
31576
31577
31578
.....
31604
31605
31606
31607
31608
31609
31610
31611
31612
31613
31614
31615
31616
31617
31618
31619
31620
31621
31622
.....
32283
32284
32285
32286
32287
32288
32289
32290
32291
32292
32293
32294
32295
32296
32297
32298
32299
32300
32301
32302
32303
32304
32305
32306
32307
32308
32309
32310
32311
32312
32313
32314
32315
32316
32317
32318
.....
32328
32329
32330
32331
32332
32333
32334

32335
32336
32337
32338
32339
32340
32341
    // - to be called if TypeNameToSimpleRTTIType() did fail, i.e. return ptCustom
    // - will return ptCustom for any unknown type
    class function TypeInfoToSimpleRTTIType(Info: pointer; ItemSize: integer): TJSONCustomParserRTTIType;
    /// unserialize some JSON content into its binary internal representation
    function ReadOneLevel(var P: PUTF8Char; var Data: PByte;
      Options: TJSONCustomParserSerializationOptions): boolean; virtual;
    /// serialize a binary internal representation into JSON content

    procedure WriteOneLevel(aWriter: TTextWriter; var P: PByte;
      Options: TJSONCustomParserSerializationOptions); virtual;
    /// the associated type name, e.g. for a record
    property CustomTypeName: RawUTF8 read fCustomTypeName;
    /// the property name
    // - may be void for the Root element
    // - e.g. 'SubProp'
................................................................................
asm // faster version by AB (direct call to finalization procedures)
        { ->    EAX pointer to record to be finalized   }
        {       EDX pointer to type info                }
        { <-    EAX pointer to record to be finalized   }
        movzx ecx,byte ptr [edx].TFieldTable.NameLen
        push ebx
        mov ebx,eax
        push esi
        push edi
        mov edi,[edx+ecx].TFieldTable.ManagedCount
        lea esi,[edx+ecx].TFieldTable.ManagedFields
        test edi,edi
        jz @@end
@@loop: mov edx,[esi].TFieldInfo.TypeInfo
        mov eax,[esi].TFieldInfo.&Offset
................................................................................
begin
  inherited;
  fNestedArray.Free;
end;

procedure TJSONCustomParserCustomSimple.CustomWriter(
  const aWriter: TTextWriter; const aValue);
var i,int: integer;

    V: PByte;
begin
  case fKnownType of
  ktStaticArray: begin
    aWriter.Add('[');
    V := @aValue;
    for i := 1 to PArrayTypeInfo(fTypeData)^.elCount do
      fNestedArray.WriteOneLevel(aWriter,V,[]);


    aWriter.CancelLastComma;
    aWriter.Add(']');
  end;
  (*  max: integer; item: PShortString;
  ktSet: begin // written as an object with set names as fields = true/false
    aWriter.Add('{');
    item := GetEnumBaseTypeList(fTypeData,max);
................................................................................
      inc(PByte(item),ord(item^[0])+1); // next short string
    end;
    aWriter.CancelLastComma;
    aWriter.Add('}');
  end;
  *)
  ktEnumeration, ktSet: begin
    int := 0;
    MoveFast(aValue,int,fDataSize);
    aWriter.AddU(int); // storing the value as binary/integer is safe and fast
    //aWriter.AddShort(GetEnumName(fCustomTypeInfo,byte(aValue))^);
  end;
  ktDynamicArray:
    raise ESynException.CreateUTF8('%.CustomWriter("%"): Unsupported',
        [self,fCustomTypeName]);
  else begin // encoded as JSON strings
    aWriter.Add('"');
................................................................................
  end;
  end;
end;

function TJSONCustomParserCustomSimple.CustomReader(P: PUTF8Char;
  var aValue; out EndOfObject: AnsiChar): PUTF8Char;
var PropValue: PUTF8Char;
    V,i: integer;

    wasString: boolean;
    Val: PByte;
begin
  result := nil; // indicates error
  case fKnownType of
  ktStaticArray: begin
    if P^<>'[' then
................................................................................
      if wasString and (TextToGUID(PropValue,@aValue)<>nil) then
        result := P;
    ktSet,ktEnumeration: begin
      if wasString then
        if fKnownType=ktSet then
          raise ESynException.CreateUTF8('%.CustomReader("%") not implemented yet from string',
            [self,fCustomTypeName]) else
          V := GetEnumNameValue(fCustomTypeInfo,PropValue,StrLen(PropValue)) else
        V := GetInteger(PropValue);
      if V<0 then
        exit;
      MoveFast(V,aValue,fDataSize);
      result := P;
    end;
    ktFixedArray:
      if wasString and (StrLen(PropValue)=fFixedSize*2) and
         SynCommons.HexToBin(PAnsiChar(PropValue),@aValue,fFixedSize) then
        result := P;
    end;
................................................................................
      inc(aWriter.fHumanReadableLevel);
      DynArray := PPointer(Value)^;
      if DynArray<>nil then
        for j := 1 to DynArrayLength(DynArray) do begin
          if soWriteHumanReadable in Options then
            aWriter.AddCRAndIndent;
          if Prop.NestedProperty[0].PropertyName='' then  // array of simple
            WriteOneValue(Prop.NestedProperty[0],DynArray) else begin
            Prop.WriteOneLevel(aWriter,DynArray,Options); // array of record
            aWriter.Add(',');
          end;
          {$ifdef ALIGNCUSTOMREC}
          if PtrUInt(DynArray)and 7<>0 then
            inc(DynArray,8-(PtrUInt(DynArray)and 7));
          {$endif}
        end;
      aWriter.CancelLastComma;
      aWriter.Add(']');
      dec(aWriter.fHumanReadableLevel);
    end;
    ptRecord: begin
      Prop.WriteOneLevel(aWriter,Value,Options);
      aWriter.Add(',');
      exit;
    end;
    ptCustom:
      TJSONCustomParserCustom(Prop).CustomWriter(aWriter,Value^);
    end;
    aWriter.Add(',');
    inc(Value,Prop.fDataSize);
  end;
var i: integer;
    SubProp: TJSONCustomParserRTTI;
begin
  if P=nil then begin
    aWriter.AddShort('null');
................................................................................
    SubProp := NestedProperty[i];
    if soWriteHumanReadable in Options then
      aWriter.AddCRAndIndent;
    aWriter.AddFieldName(SubProp.PropertyName);
    if soWriteHumanReadable in Options then
      aWriter.Add(' ');
    WriteOneValue(SubProp,P);

  end;
  aWriter.CancelLastComma;
  dec(aWriter.fHumanReadableLevel);
  if soWriteHumanReadable in Options then
    aWriter.AddCRAndIndent;
  aWriter.Add('}');
end;






>







 







|







 







|
>






|

>
>







 







|
|
|







 







|
>







 







|
|
|

|







 







|

|
<











<





<







 







>







5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
.....
30137
30138
30139
30140
30141
30142
30143
30144
30145
30146
30147
30148
30149
30150
30151
.....
31514
31515
31516
31517
31518
31519
31520
31521
31522
31523
31524
31525
31526
31527
31528
31529
31530
31531
31532
31533
31534
31535
31536
31537
31538
31539
.....
31544
31545
31546
31547
31548
31549
31550
31551
31552
31553
31554
31555
31556
31557
31558
31559
31560
.....
31568
31569
31570
31571
31572
31573
31574
31575
31576
31577
31578
31579
31580
31581
31582
31583
.....
31609
31610
31611
31612
31613
31614
31615
31616
31617
31618
31619
31620
31621
31622
31623
31624
31625
31626
31627
.....
32288
32289
32290
32291
32292
32293
32294
32295
32296
32297

32298
32299
32300
32301
32302
32303
32304
32305
32306
32307
32308

32309
32310
32311
32312
32313

32314
32315
32316
32317
32318
32319
32320
.....
32330
32331
32332
32333
32334
32335
32336
32337
32338
32339
32340
32341
32342
32343
32344
    // - to be called if TypeNameToSimpleRTTIType() did fail, i.e. return ptCustom
    // - will return ptCustom for any unknown type
    class function TypeInfoToSimpleRTTIType(Info: pointer; ItemSize: integer): TJSONCustomParserRTTIType;
    /// unserialize some JSON content into its binary internal representation
    function ReadOneLevel(var P: PUTF8Char; var Data: PByte;
      Options: TJSONCustomParserSerializationOptions): boolean; virtual;
    /// serialize a binary internal representation into JSON content
    // - this method won't append a trailing ',' character
    procedure WriteOneLevel(aWriter: TTextWriter; var P: PByte;
      Options: TJSONCustomParserSerializationOptions); virtual;
    /// the associated type name, e.g. for a record
    property CustomTypeName: RawUTF8 read fCustomTypeName;
    /// the property name
    // - may be void for the Root element
    // - e.g. 'SubProp'
................................................................................
asm // faster version by AB (direct call to finalization procedures)
        { ->    EAX pointer to record to be finalized   }
        {       EDX pointer to type info                }
        { <-    EAX pointer to record to be finalized   }
        movzx ecx,byte ptr [edx].TFieldTable.NameLen
        push ebx
        mov ebx,eax
        push esi         
        push edi
        mov edi,[edx+ecx].TFieldTable.ManagedCount
        lea esi,[edx+ecx].TFieldTable.ManagedFields
        test edi,edi
        jz @@end
@@loop: mov edx,[esi].TFieldInfo.TypeInfo
        mov eax,[esi].TFieldInfo.&Offset
................................................................................
begin
  inherited;
  fNestedArray.Free;
end;

procedure TJSONCustomParserCustomSimple.CustomWriter(
  const aWriter: TTextWriter; const aValue);
var i: integer;
    i64: Int64;
    V: PByte;
begin
  case fKnownType of
  ktStaticArray: begin
    aWriter.Add('[');
    V := @aValue;
    for i := 1 to PArrayTypeInfo(fTypeData)^.elCount do begin
      fNestedArray.WriteOneLevel(aWriter,V,[]);
      aWriter.Add(',');
    end;
    aWriter.CancelLastComma;
    aWriter.Add(']');
  end;
  (*  max: integer; item: PShortString;
  ktSet: begin // written as an object with set names as fields = true/false
    aWriter.Add('{');
    item := GetEnumBaseTypeList(fTypeData,max);
................................................................................
      inc(PByte(item),ord(item^[0])+1); // next short string
    end;
    aWriter.CancelLastComma;
    aWriter.Add('}');
  end;
  *)
  ktEnumeration, ktSet: begin
    i64 := 0;
    MoveFast(aValue,i64,fDataSize);
    aWriter.Add(i64); // storing the value as binary/integer is safe and fast
    //aWriter.AddShort(GetEnumName(fCustomTypeInfo,byte(aValue))^);
  end;
  ktDynamicArray:
    raise ESynException.CreateUTF8('%.CustomWriter("%"): Unsupported',
        [self,fCustomTypeName]);
  else begin // encoded as JSON strings
    aWriter.Add('"');
................................................................................
  end;
  end;
end;

function TJSONCustomParserCustomSimple.CustomReader(P: PUTF8Char;
  var aValue; out EndOfObject: AnsiChar): PUTF8Char;
var PropValue: PUTF8Char;
    i: integer;
    i64: Int64;
    wasString: boolean;
    Val: PByte;
begin
  result := nil; // indicates error
  case fKnownType of
  ktStaticArray: begin
    if P^<>'[' then
................................................................................
      if wasString and (TextToGUID(PropValue,@aValue)<>nil) then
        result := P;
    ktSet,ktEnumeration: begin
      if wasString then
        if fKnownType=ktSet then
          raise ESynException.CreateUTF8('%.CustomReader("%") not implemented yet from string',
            [self,fCustomTypeName]) else
          i64 := GetEnumNameValue(fCustomTypeInfo,PropValue,StrLen(PropValue)) else
        i64 := GetInt64(PropValue);
      if i64<0 then
        exit;
      MoveFast(i64,aValue,fDataSize);
      result := P;
    end;
    ktFixedArray:
      if wasString and (StrLen(PropValue)=fFixedSize*2) and
         SynCommons.HexToBin(PAnsiChar(PropValue),@aValue,fFixedSize) then
        result := P;
    end;
................................................................................
      inc(aWriter.fHumanReadableLevel);
      DynArray := PPointer(Value)^;
      if DynArray<>nil then
        for j := 1 to DynArrayLength(DynArray) do begin
          if soWriteHumanReadable in Options then
            aWriter.AddCRAndIndent;
          if Prop.NestedProperty[0].PropertyName='' then  // array of simple
            WriteOneValue(Prop.NestedProperty[0],DynArray) else
            Prop.WriteOneLevel(aWriter,DynArray,Options); // array of record
          aWriter.Add(',');

          {$ifdef ALIGNCUSTOMREC}
          if PtrUInt(DynArray)and 7<>0 then
            inc(DynArray,8-(PtrUInt(DynArray)and 7));
          {$endif}
        end;
      aWriter.CancelLastComma;
      aWriter.Add(']');
      dec(aWriter.fHumanReadableLevel);
    end;
    ptRecord: begin
      Prop.WriteOneLevel(aWriter,Value,Options);

      exit;
    end;
    ptCustom:
      TJSONCustomParserCustom(Prop).CustomWriter(aWriter,Value^);
    end;

    inc(Value,Prop.fDataSize);
  end;
var i: integer;
    SubProp: TJSONCustomParserRTTI;
begin
  if P=nil then begin
    aWriter.AddShort('null');
................................................................................
    SubProp := NestedProperty[i];
    if soWriteHumanReadable in Options then
      aWriter.AddCRAndIndent;
    aWriter.AddFieldName(SubProp.PropertyName);
    if soWriteHumanReadable in Options then
      aWriter.Add(' ');
    WriteOneValue(SubProp,P);
    aWriter.Add(',');
  end;
  aWriter.CancelLastComma;
  dec(aWriter.fHumanReadableLevel);
  if soWriteHumanReadable in Options then
    aWriter.AddCRAndIndent;
  aWriter.Add('}');
end;

Changes to SynopseCommit.inc.

1
'1.18.1974'
|
1
'1.18.1975'