Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
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: |
c55e03ca6df5946240b39fded57f240c |
User & Date: | ab 2015-05-06 19:16:28 |
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 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'
|