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

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

Overview
Comment:{1305} fixed TSQLRawBlob serialization as part of ObjectToJSON() - also some refactor, for slightly speed enhancement and code simplification
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c55e03ca6df5946240b39fded57f240ce26262c4
User & Date: ab 2015-05-06 19:16:28
Context
2015-05-06
19:34
{1306} fixed small regression identified by TTestExternalDatabase.DBPropertiesPersistence test check-in: ad27dbd9b6 user: ab tags: trunk
19:16
{1305} fixed TSQLRawBlob serialization as part of ObjectToJSON() - also some refactor, for slightly speed enhancement and code simplification check-in: c55e03ca6d user: ab tags: trunk
12:22
{1304} restrained paranoid exception when a TSQLRecord field name may match a SQLite3 keyword check-in: bebd2b95a8 user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/mORMot.pas.

47002
47003
47004
47005
47006
47007
47008
47009
47010
47011
47012
47013
47014
47015
47016
         WR.Add(PInteger(V)^) else
         WR.AddU(PCardinal(V)^);
    8: WR.Add(PInt64(V)^);
  end;
  smvDouble, smvDateTime: WR.AddDouble(PDouble(V)^);
  smvCurrency:   WR.AddCurr64(PInt64(V)^);
  smvRawUTF8:    WR.AddJSONEscape(PPointer(V)^);
  smvRawJSON:    WR.AddNoJSONEscape(PPointer(V)^);
  smvString:     {$ifdef UNICODE}
                 WR.AddJSONEscapeW(pointer(PString(V)^));
                 {$else}
                 WR.AddJSONEscapeAnsiString(PString(V)^);
                 {$endif}
  smvWideString: WR.AddJSONEscapeW(PPointer(V)^);
  smvObject:     WR.WriteObject(PPointer(V)^,[]);






|







47002
47003
47004
47005
47006
47007
47008
47009
47010
47011
47012
47013
47014
47015
47016
         WR.Add(PInteger(V)^) else
         WR.AddU(PCardinal(V)^);
    8: WR.Add(PInt64(V)^);
  end;
  smvDouble, smvDateTime: WR.AddDouble(PDouble(V)^);
  smvCurrency:   WR.AddCurr64(PInt64(V)^);
  smvRawUTF8:    WR.AddJSONEscape(PPointer(V)^);
  smvRawJSON:    WR.AddNoJSONEscape(PPointer(V)^,length(PRawUTF8(V)^));
  smvString:     {$ifdef UNICODE}
                 WR.AddJSONEscapeW(pointer(PString(V)^));
                 {$else}
                 WR.AddJSONEscapeAnsiString(PString(V)^);
                 {$endif}
  smvWideString: WR.AddJSONEscapeW(PPointer(V)^);
  smvObject:     WR.WriteObject(PPointer(V)^,[]);

Changes to SynBidirSock.pas.

939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
    WR.Add('"');
    WR.AddString(ContentType);
    WR.Add('"',',');
    if Content='' then
      WR.Add('"','"') else
    if (ContentType='') or
       IdemPropNameU(ContentType,JSON_CONTENT_TYPE) then
      WR.AddNoJSONEscape(pointer(Content)) else
    if IdemPChar(pointer(ContentType),'TEXT/') then
      WR.AddCSVUTF8([Content]) else
      WR.WrBase64(pointer(Content),length(Content),true);
    WR.Add(']','}');
    WR.SetText(RawUTF8(frame.payload));
  finally
    WR.Free;






|







939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
    WR.Add('"');
    WR.AddString(ContentType);
    WR.Add('"',',');
    if Content='' then
      WR.Add('"','"') else
    if (ContentType='') or
       IdemPropNameU(ContentType,JSON_CONTENT_TYPE) then
      WR.AddNoJSONEscape(pointer(Content),length(Content)) else
    if IdemPChar(pointer(ContentType),'TEXT/') then
      WR.AddCSVUTF8([Content]) else
      WR.WrBase64(pointer(Content),length(Content),true);
    WR.Add(']','}');
    WR.SetText(RawUTF8(frame.payload));
  finally
    WR.Free;

Changes to SynCommons.pas.

5945
5946
5947
5948
5949
5950
5951
5952
5953



5954
5955
5956
5957
5958
5959
5960
5961
.....
22919
22920
22921
22922
22923
22924
22925
22926
22927
22928
22929
22930
22931
22932
22933
22934
22935
.....
35435
35436
35437
35438
35439
35440
35441
35442
35443
35444
35445
35446
35447
35448
35449
.....
36041
36042
36043
36044
36045
36046
36047





36048
36049
36050
36051
36052
36053
36054
36055
36056
36057
36058
36059
36060
36061
36062
36063
36064
36065
36066
36067
36068
36069
36070
36071
36072
36073
36074
36075
36076
36077
36078
36079
36080
36081

36082


36083
36084
36085
36086
36087
36088
36089
36090
36091
36092
36093
36094
36095
36096
36097
.....
36132
36133
36134
36135
36136
36137
36138
36139
36140
36141
36142
36143
36144
36145
36146
.....
36173
36174
36175
36176
36177
36178
36179
36180
36181
36182
36183
36184
36185
36186
36187
.....
36188
36189
36190
36191
36192
36193
36194
36195
36196
36197
36198
36199
36200
36201
36202
    // - if CodePage is left to its default value of -1, it will assume
    // CurrentAnsiConvert.CodePage prior to Delphi 2009, but newer UNICODE
    // versions of Delphi will retrieve the code page from string
    // - if CodePage is defined to a >= 0 value, the encoding will take place
    procedure AddAnyAnsiString(const s: RawByteString; Escape: TTextWriterKind;
      CodePage: Integer=-1);
    /// append some chars to the buffer
    // - if Len is 0, Len is calculated from zero-ended char
    // - don't escapes chars according to the JSON RFC



    procedure AddNoJSONEscape(P: Pointer; Len: integer=0); overload;
    /// append some chars, quoting all " chars
    // - same algorithm than AddString(QuotedStr()) - without memory allocation
    // - this function implements what is specified in the official SQLite3
    // documentation: "A string constant is formed by enclosing the string in single
    // quotes ('). A single quote within the string can be encoded by putting two
    // single quotes in a row - as in Pascal."
    procedure AddQuotedStr(Text: PUTF8Char; Quote: AnsiChar; TextLen: integer=0);
................................................................................
begin
  if length(NameValuePairs)<2 then
    result := '' else
    with TTextWriter.CreateOwnedStream do
    try
      for i := 1 to length(NameValuePairs) shr 1 do begin
        Add(NameValuePairs[i*2-2],twNone);
        AddNoJSONEscape(pointer(KeySeparator));
        Add(NameValuePairs[i*2-1],twNone);
        AddNoJSONEscape(pointer(ValueSeparator));
      end;
      SetText(result);
    finally
      Free;
    end;
end;

................................................................................
    if JSON^='}' then
      repeat inc(JSON) until not(JSON^ in [#1..' ']) else
    repeat
      Name := GetJSONPropName(JSON);
      if Name=nil then
        exit;
      if (Format=jsonUnquotedPropName) and PropNameValid(Name) then
        AddNoJSONEscape(Name) else begin
        Add('"');
        AddJSONEscape(Name);
        Add('"');
      end;
      if Format=jsonCompact then
        Add(':') else
        Add(':',' ');
................................................................................
function Chars3ToInt18(P: pointer): cardinal;
begin
  result := PCardinal(P)^-$202020;
  result := ((result shr 16)and $3f)+
            ((result shr 8) and $3f)shl 6+
            (result and $3f)shl 12;
end;






procedure TTextWriter.AddNoJSONEscape(P: Pointer; Len: integer);
var i: integer;
begin
  if P=nil then exit;
  if Len=0 then
    Len := StrLen(PUTF8Char(P));
  if Len>0 then begin // no JSONify:
    if B+8>=BEnd then
      FlushToStream;
    inc(B); // allow CancelLastChar
    case Len of
      1: B^ := PAnsiChar(P)^;
      2: begin PWord(B)^ := PWord(P)^; Inc(B); end;
      3: begin PWord(B)^ := PWord(P)^; B[2] := PAnsiChar(P)[2]; Inc(B,2); end;
      4: begin PCardinal(B)^ := PCardinal(P)^; Inc(B,3); end;
      5: begin PCardinal(B)^ := PCardinal(P)^; B[4] := PAnsiChar(P)[4]; Inc(B,4); end;
      6: begin PCardinal(B)^ := PCardinal(P)^; PWordArray(B)[2] := PWordArray(P)[2];
          Inc(B,5); end;
      7: begin PCardinal(B)^ := PCardinal(P)^; PWordArray(B)[2] := PWordArray(P)[2];
          B[6] := PAnsiChar(P)[6]; Inc(B,6); end;
      8: begin PInt64(B)^ := PInt64(P)^; inc(B,7); end;
      else begin
        repeat
          // guess biggest size to be added into buf^ at once
          i := BEnd-B;
          if Len<i then
            i := Len;
          // add UTF-8 bytes
          move(P^,B^,i);
          inc(PtrInt(P),i);
          inc(B,i);
          dec(Len,i);
          if Len=0 then

            break;


          // FlushInc writes B-buf+1 -> special one below:
          inc(fTotalFileSize,fStream.Write(fTempBuf^,B-fTempBuf));
          B := fTempBuf;
        until false;
        dec(B); // allow CancelLastChar
      end;
    end;
  end;
end;

procedure TTextWriter.AddNoJSONEscapeW(WideChar: PWord; WideCharCount: integer);
var PEnd: PtrUInt;
    BMax: PUTF8Char;
begin
  if WideChar=nil then
................................................................................
  end;
end;

procedure TTextWriter.Add(P: PUTF8Char; Escape: TTextWriterKind);
begin
  if P<>nil then
  case Escape of
    twNone:       AddNoJSONEscape(P);
    twJSONEscape: AddJSONEscape(P);
    twOnSameLine: AddOnSameLine(P);
  end;
end;

procedure TTextWriter.Add(P: PUTF8Char; Len: PtrInt; Escape: TTextWriterKind);
begin
................................................................................
    tmpU8: array[0..256*3] of AnsiChar;
    U8: PUTF8Char;
begin
  L := length(s);
  if L=0 then
    exit;
  if PInteger(s)^ and $ffffff=JSON_BASE64_MAGIC then begin
    WrBase64(pointer(s),L,false); // identified as a BLOB content
    exit;
  end;
  if CodePage<0 then
    {$ifdef UNICODE}
    CodePage := StringCodePage(s);
    {$else}
    CodePage := 0;
................................................................................
    {$endif}
  case CodePage of
  CP_UTF8, CP_RAWBYTESTRING:
    Add(pointer(s),0,Escape);  // direct write of RawUTF8/RawByteString content
  CP_UTF16:
    AddW(pointer(s),0,Escape); // direct write of UTF-16 content
  CP_SQLRAWBLOB: begin
    AddNoJSONEscape(@JSON_BASE64_MAGIC_QUOTE_VAR,4);
    WrBase64(pointer(s),L,false);
  end;
  else begin
    if L>=SizeOf(tmpU8)div 3 then
      Getmem(U8,L*3+1) else
      U8 := @tmpU8;
    L := TSynAnsiConvert.Engine(CodePage).AnsiBufferToUTF8(U8,pointer(s),L)-U8;






|

>
>
>
|







 







|

|







 







|







 







>
>
>
>
>




|
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
|
|
|
|
<
|
<
<
>
|
>
>
|
|
|
|
|
|
<
<







 







|







 







|







 







|







5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
.....
22922
22923
22924
22925
22926
22927
22928
22929
22930
22931
22932
22933
22934
22935
22936
22937
22938
.....
35438
35439
35440
35441
35442
35443
35444
35445
35446
35447
35448
35449
35450
35451
35452
.....
36044
36045
36046
36047
36048
36049
36050
36051
36052
36053
36054
36055
36056
36057
36058
36059
36060





36061












36062
36063

36064
36065
36066
36067

36068


36069
36070
36071
36072
36073
36074
36075
36076
36077
36078


36079
36080
36081
36082
36083
36084
36085
.....
36120
36121
36122
36123
36124
36125
36126
36127
36128
36129
36130
36131
36132
36133
36134
.....
36161
36162
36163
36164
36165
36166
36167
36168
36169
36170
36171
36172
36173
36174
36175
.....
36176
36177
36178
36179
36180
36181
36182
36183
36184
36185
36186
36187
36188
36189
36190
    // - if CodePage is left to its default value of -1, it will assume
    // CurrentAnsiConvert.CodePage prior to Delphi 2009, but newer UNICODE
    // versions of Delphi will retrieve the code page from string
    // - if CodePage is defined to a >= 0 value, the encoding will take place
    procedure AddAnyAnsiString(const s: RawByteString; Escape: TTextWriterKind;
      CodePage: Integer=-1);
    /// append some chars to the buffer
    // - input length is calculated from zero-ended char
    // - don't escapes chars according to the JSON RFC
    procedure AddNoJSONEscape(P: Pointer); overload;
    /// append some chars to the buffer
    // - don't escapes chars according to the JSON RFC
    procedure AddNoJSONEscape(P: Pointer; Len: integer); overload;
    /// append some chars, quoting all " chars
    // - same algorithm than AddString(QuotedStr()) - without memory allocation
    // - this function implements what is specified in the official SQLite3
    // documentation: "A string constant is formed by enclosing the string in single
    // quotes ('). A single quote within the string can be encoded by putting two
    // single quotes in a row - as in Pascal."
    procedure AddQuotedStr(Text: PUTF8Char; Quote: AnsiChar; TextLen: integer=0);
................................................................................
begin
  if length(NameValuePairs)<2 then
    result := '' else
    with TTextWriter.CreateOwnedStream do
    try
      for i := 1 to length(NameValuePairs) shr 1 do begin
        Add(NameValuePairs[i*2-2],twNone);
        AddNoJSONEscape(pointer(KeySeparator),length(KeySeparator));
        Add(NameValuePairs[i*2-1],twNone);
        AddNoJSONEscape(pointer(ValueSeparator),length(ValueSeparator));
      end;
      SetText(result);
    finally
      Free;
    end;
end;

................................................................................
    if JSON^='}' then
      repeat inc(JSON) until not(JSON^ in [#1..' ']) else
    repeat
      Name := GetJSONPropName(JSON);
      if Name=nil then
        exit;
      if (Format=jsonUnquotedPropName) and PropNameValid(Name) then
        AddNoJSONEscape(Name,StrLen(Name)) else begin
        Add('"');
        AddJSONEscape(Name);
        Add('"');
      end;
      if Format=jsonCompact then
        Add(':') else
        Add(':',' ');
................................................................................
function Chars3ToInt18(P: pointer): cardinal;
begin
  result := PCardinal(P)^-$202020;
  result := ((result shr 16)and $3f)+
            ((result shr 8) and $3f)shl 6+
            (result and $3f)shl 12;
end;

procedure TTextWriter.AddNoJSONEscape(P: Pointer);
begin
  AddNoJSONEscape(P,StrLen(PUTF8Char(P)));
end;

procedure TTextWriter.AddNoJSONEscape(P: Pointer; Len: integer);
var i: integer;
begin
  if (P<>nil) and (Len>0) then begin





    inc(B); // allow CancelLastChar












    repeat
      i := BEnd-B+1; // guess biggest size to be added into buf^ at once

      if Len<i then
        i := Len;
      // add UTF-8 bytes
      move(P^,B^,i);

      inc(B,i);


      if i=Len then
        break;
      inc(PtrInt(P),i);
      dec(Len,i);
      // FlushInc writes B-buf+1 -> special one below:
      inc(fTotalFileSize,fStream.Write(fTempBuf^,B-fTempBuf));
      B := fTempBuf;
    until false;
    dec(B); // allow CancelLastChar
  end; 


end;

procedure TTextWriter.AddNoJSONEscapeW(WideChar: PWord; WideCharCount: integer);
var PEnd: PtrUInt;
    BMax: PUTF8Char;
begin
  if WideChar=nil then
................................................................................
  end;
end;

procedure TTextWriter.Add(P: PUTF8Char; Escape: TTextWriterKind);
begin
  if P<>nil then
  case Escape of
    twNone:       AddNoJSONEscape(P,StrLen(P));
    twJSONEscape: AddJSONEscape(P);
    twOnSameLine: AddOnSameLine(P);
  end;
end;

procedure TTextWriter.Add(P: PUTF8Char; Len: PtrInt; Escape: TTextWriterKind);
begin
................................................................................
    tmpU8: array[0..256*3] of AnsiChar;
    U8: PUTF8Char;
begin
  L := length(s);
  if L=0 then
    exit;
  if PInteger(s)^ and $ffffff=JSON_BASE64_MAGIC then begin
    AddNoJSONEscape(pointer(s),L); // identified as a BLOB content
    exit;
  end;
  if CodePage<0 then
    {$ifdef UNICODE}
    CodePage := StringCodePage(s);
    {$else}
    CodePage := 0;
................................................................................
    {$endif}
  case CodePage of
  CP_UTF8, CP_RAWBYTESTRING:
    Add(pointer(s),0,Escape);  // direct write of RawUTF8/RawByteString content
  CP_UTF16:
    AddW(pointer(s),0,Escape); // direct write of UTF-16 content
  CP_SQLRAWBLOB: begin
    AddNoJSONEscape(@PByteArray(@JSON_BASE64_MAGIC_QUOTE_VAR)[1],3);
    WrBase64(pointer(s),L,false);
  end;
  else begin
    if L>=SizeOf(tmpU8)div 3 then
      Getmem(U8,L*3+1) else
      U8 := @tmpU8;
    L := TSynAnsiConvert.Engine(CodePage).AnsiBufferToUTF8(U8,pointer(s),L)-U8;

Changes to SynCrtSock.pas.

2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
      end;
      if Length=0 then
        exit; // we got everything we wanted
      if not UseOnlySockIn then
        break;
      if InputSock(PTextRec(SockIn)^)<0 then 
        raise ECrtSocket.Create('SockInRead InputSock');
    until false;
  // direct receiving of the remaining bytes from socket
  if Length>0 then begin
    SockRecv(Content,Length);
    inc(result,Length);
  end;
end;







|







2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
      end;
      if Length=0 then
        exit; // we got everything we wanted
      if not UseOnlySockIn then
        break;
      if InputSock(PTextRec(SockIn)^)<0 then 
        raise ECrtSocket.Create('SockInRead InputSock');
    until Timeout=0;
  // direct receiving of the remaining bytes from socket
  if Length>0 then begin
    SockRecv(Content,Length);
    inc(result,Length);
  end;
end;

Changes to SynMustache.pas.

1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  if TVarData(Value).VType>varNull then
    if VarIsNumeric(Value) then // avoid RawUTF8 conversion for plain numbers
      fWriter.AddVariantJSON(Value,twNone) else begin
      if fEscapeInvert then
        UnEscape := not UnEscape;
      VariantToUTF8(Value,ValueText,wasString);
      if UnEscape then
        fWriter.AddNoJSONEscape(pointer(ValueText)) else
        fWriter.AddHtmlEscape(pointer(ValueText));
    end;
end;

function TSynMustacheContextVariant.AppendSection(
  const ValueName: RawUTF8): TSynMustacheSectionType;
var Value: TVarData;






|







1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  if TVarData(Value).VType>varNull then
    if VarIsNumeric(Value) then // avoid RawUTF8 conversion for plain numbers
      fWriter.AddVariantJSON(Value,twNone) else begin
      if fEscapeInvert then
        UnEscape := not UnEscape;
      VariantToUTF8(Value,ValueText,wasString);
      if UnEscape then
        fWriter.AddNoJSONEscape(pointer(ValueText),length(ValueText)) else
        fWriter.AddHtmlEscape(pointer(ValueText));
    end;
end;

function TSynMustacheContextVariant.AppendSection(
  const ValueName: RawUTF8): TSynMustacheSectionType;
var Value: TVarData;

Changes to SynSelfTests.pas.

4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464

4465
4466
4467
4468
4469
4470
4471
....
5030
5031
5032
5033
5034
5035
5036






















5037
5038
5039
5040
5041
5042
5043
....
5269
5270
5271
5272
5273
5274
5275
5276

5277
5278
5279
5280
5281
5282
5283
  __TSubCD = 'c : byte; d : RawUTF8;';
  __TAggregate = 'abArr : array of TSubAB; cdArr : array of TSubCD;';

  zendframeworkFileName = 'zendframework.json';
  discogsFileName = 'discogs.json';

procedure TTestLowLevelTypes.EncodeDecodeJSON;
var J,U: RawUTF8;
    binary,zendframeworkJson,discogsJson: RawByteString;
    V: TPUtf8CharDynArray;
    i, a, err: integer;
    r: Double;

    Parser: TJSONRecordTextDefinition;
    JR,JR2: TTestCustomJSONRecord;
    JA,JA2: TTestCustomJSONArray;
    JAS: TTestCustomJSONArraySimple;
{$ifndef NOVARIANTS}
    JAV: TTestCustomJSONArrayVariant;
{$endif}
................................................................................
  Check(JSONEncode('{type:{$in:?}}',[],[_Arr(['food','snack'])])=J);
  J := JSONEncode('{name:"John",field:{ "$regex": "acme.*corp", $options: "i" }}',[],[]);
  Check(J='{"name":"John","field":{"$regex":"acme.*corp","$options":"i"}}');
  // the below only works if unit SynMongoDB is included in the uses list of the project
  // for virtual function TryJSONToVariant
  Check(J=JSONEncode('{name:?,field:/%/i}',['acme.*corp'],['John']));
{$endif}






















  for i := 1 to 100 do begin
    a := Random(maxInt);
    r := Random;
    U := RandomUTF8(i);
    J := JSONEncode(['a',a,'r',r,'u',U]);
    JSONDecode(J,['U','R','A','FOO'],V);
    Check(Length(V)=4);
................................................................................
     Coll.Str.BeginUpdate;
     for i := 1 to 10000 do
       Check(Coll.Str.Add(IntToStr(i))=i-1);
     Coll.Str.EndUpdate;
     U := ObjectToJSON(Coll);
     Check(Hash32(U)=$85926050);
     J := ObjectToJSON(Coll,[woHumanReadable]);
     Check(JSONReformat(J,jsonCompact)=U);

     C2.Str := TStringList.Create;
     Check(JSONToObject(C2,pointer(U),Valid)=nil);
     Check(Valid);
     Check(C2.Str.Count=Coll.Str.Count);
     for i := 1 to C2.Str.Count do
       Check(C2.Str[i-1]=IntToStr(i));
     J := ObjectToJSON(C2);






|




>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>







4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
....
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
....
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
  __TSubCD = 'c : byte; d : RawUTF8;';
  __TAggregate = 'abArr : array of TSubAB; cdArr : array of TSubCD;';

  zendframeworkFileName = 'zendframework.json';
  discogsFileName = 'discogs.json';

procedure TTestLowLevelTypes.EncodeDecodeJSON;
var J,U,U2: RawUTF8;
    binary,zendframeworkJson,discogsJson: RawByteString;
    V: TPUtf8CharDynArray;
    i, a, err: integer;
    r: Double;
    peop: TSQLRecordPeople;
    Parser: TJSONRecordTextDefinition;
    JR,JR2: TTestCustomJSONRecord;
    JA,JA2: TTestCustomJSONArray;
    JAS: TTestCustomJSONArraySimple;
{$ifndef NOVARIANTS}
    JAV: TTestCustomJSONArrayVariant;
{$endif}
................................................................................
  Check(JSONEncode('{type:{$in:?}}',[],[_Arr(['food','snack'])])=J);
  J := JSONEncode('{name:"John",field:{ "$regex": "acme.*corp", $options: "i" }}',[],[]);
  Check(J='{"name":"John","field":{"$regex":"acme.*corp","$options":"i"}}');
  // the below only works if unit SynMongoDB is included in the uses list of the project
  // for virtual function TryJSONToVariant
  Check(J=JSONEncode('{name:?,field:/%/i}',['acme.*corp'],['John']));
{$endif}
  peop := TSQLRecordPeople.Create;
  try
    peop.IDValue := 1234;
    peop.FirstName := 'FN';
    peop.LastName := 'LN';
    peop.YearOfBirth := 1000;
    peop.Data := #1#2#3#4;
    J := ObjectToJSON(peop,[woSQLRawBlobAsBase64]);
    check(J[53]=#$EF);
    check(J[54]=#$BF);
    check(J[55]=#$B0);
    J[53] := '1';
    J[54] := '2';
    J[55] := '3';
    Check(J='{"ID":1234,"FirstName":"FN","LastName":"LN",'+
      '"Data":"123AQIDBA==","YearOfBirth":1000,"YearOfDeath":0}');
    J := ObjectToJSON(peop);
    Check(J='{"ID":1234,"FirstName":"FN","LastName":"LN",'+
      '"Data":"","YearOfBirth":1000,"YearOfDeath":0}');
  finally
    peop.Free;
  end;
  for i := 1 to 100 do begin
    a := Random(maxInt);
    r := Random;
    U := RandomUTF8(i);
    J := JSONEncode(['a',a,'r',r,'u',U]);
    JSONDecode(J,['U','R','A','FOO'],V);
    Check(Length(V)=4);
................................................................................
     Coll.Str.BeginUpdate;
     for i := 1 to 10000 do
       Check(Coll.Str.Add(IntToStr(i))=i-1);
     Coll.Str.EndUpdate;
     U := ObjectToJSON(Coll);
     Check(Hash32(U)=$85926050);
     J := ObjectToJSON(Coll,[woHumanReadable]);
     U2 := JSONReformat(J,jsonCompact);
     Check(U2=U);
     C2.Str := TStringList.Create;
     Check(JSONToObject(C2,pointer(U),Valid)=nil);
     Check(Valid);
     Check(C2.Str.Count=Coll.Str.Count);
     for i := 1 to C2.Str.Count do
       Check(C2.Str[i-1]=IntToStr(i));
     J := ObjectToJSON(C2);

Changes to SynopseCommit.inc.

1
'1.18.1304'
|
1
'1.18.1305'