Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | fixed unexpected GPF error when clearing existing record content during JSON unserialization |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
6e28c20745e734e6a840d2f9cacb9a28 |
User & Date: | User 2013-12-20 10:52:43 |
2013-12-20
| ||
11:04 | fixed another potential GPF issue check-in: 98387083f9 user: User tags: trunk | |
10:52 | fixed unexpected GPF error when clearing existing record content during JSON unserialization check-in: 6e28c20745 user: User tags: trunk | |
2013-12-18
| ||
18:44 | now text-defined JSON record serialization is able to handle any pre-defined record types - added regression tests and updated documentation check-in: b0371f5e5a user: User tags: trunk | |
Changes to SynCommons.pas.
21834 21835 21836 21837 21838 21839 21840 21841 21842 21843 21844 21845 21846 21847 21848 21849 21850 21851 21852 21853 21854 21855 21856 21857 21858 21859 21860 ..... 21880 21881 21882 21883 21884 21885 21886 21887 21888 21889 21890 21891 21892 21893 21894 21895 21896 ..... 21935 21936 21937 21938 21939 21940 21941 21942 21943 21944 21945 21946 21947 21948 ..... 21983 21984 21985 21986 21987 21988 21989 21990 21991 21992 21993 21994 21995 21996 21997 ..... 22013 22014 22015 22016 22017 22018 22019 22020 22021 22022 22023 22024 22025 22026 22027 ..... 22136 22137 22138 22139 22140 22141 22142 22143 22144 22145 22146 22147 22148 22149 22150 22151 22152 22153 22154 22155 22156 22157 22158 22159 |
raise ESynException.CreateFmt('"%s" type should not have been un-registered', [fRecordTypeName]); end; { TJSONCustomParserRTTI } const // binary size (in bytes) of each kind of property - 0 for ptRecord/ptCustom JSONRTTI_SIZE: array[TJSONCustomParserRTTIType] of byte = ( SizeOf(PtrUInt),SizeOf(Boolean),SizeOf(Byte),SizeOf(Cardinal),SizeOf(Currency), SizeOf(Double),SizeOf(Int64),SizeOf(Integer),SizeOf(RawUTF8),0,SizeOf(Single), SizeOf(String),SizeOf(SynUnicode),SizeOf(TDateTime),SizeOf(TGUID), SizeOf(TTimeLog),SizeOf(WideString),SizeOf(Word),0); /// if defined, will try to mimic the default record alignment // -> is buggy, and compiler revision specific -> we would rather use packed records {.$define ALIGNCUSTOMREC} procedure TJSONCustomParserRTTI.ComputeDataSizeAfterAdd; var i: integer; begin if fDataSize=0 then begin assert(fFullDataSize=0); for i := 0 to high(NestedProperty) do begin NestedProperty[i].ComputeDataSizeAfterAdd; inc(fFullDataSize,NestedProperty[i].fDataSize); ................................................................................ case NestedProperty[j].PropertyType of ptRawUTF8: PRawUTF8(Data)^ := ''; ptString: PString(Data)^ := ''; ptSynUnicode: PSynUnicode(Data)^ := ''; ptWideString: PWideString(Data)^ := ''; ptArray: NestedProperty[j].FinalizeNestedArray(PPtrUInt(Data)^); ptRecord: NestedProperty[j].FinalizeNestedRecord(Data); ptCustom: RecordClear(Data,TJSONCustomParserCustom(self).fRecordTypeInfo); end; inc(Data,JSONRTTI_SIZE[NestedProperty[j].PropertyType]); end; end; procedure TJSONCustomParserRTTI.FinalizeNestedArray(var Data: PtrUInt); var i: integer; Rec: ^TDynArrayRec; ItemData: PByte; ................................................................................ case Prop.PropertyType of ptRecord: begin if not Prop.ReadOneLevel(P,Data,Options) then exit; EndOfObject := P^; if P^ in [',','}'] then inc(P); end; ptArray: begin if P^<>'[' then exit; // we expect a true array here repeat inc(P) until P^<>' '; // allocate nested array at once ArrayLen := JSONArrayCount(P); ................................................................................ end; end; ptCustom: begin P := TJSONCustomParserCustom(Prop).GetJSONCustomParserRegistration. Reader(P,Data^,valid); if (P=nil) or not valid then exit; inc(Data,Prop.fDataSize); EndOfObject := P^; if P^ in [',','}'] then inc(P); end; else begin PropValue := GetJSONField(P,P,@wasString,@EndOfObject); if (PropValue=nil) or (wasString<>(Prop.PropertyType in ................................................................................ ptTimeLog: PInt64(Data)^ := Iso8601ToSecondsPUTF8Char(PropValue,0); ptWideString:UTF8ToWideString(PropValue,StrLen(PropValue),PWideString(Data)^); ptWord: PWord(Data)^ := GetCardinal(PropValue); ptGUID: TextToGUID(PropValue,pointer(Data)); end; end; end; inc(Data,JSONRTTI_SIZE[Prop.PropertyType]); result := true; end; var i,j: integer; PropName: RawUTF8; wasString: boolean; Values: array of PUTF8Char; begin ................................................................................ inc(DynArray,8-(PtrUInt(DynArray)and 7); {$endif} end; aWriter.CancelLastComma; aWriter.Add(']'); dec(aWriter.fHumanReadableLevel); end; ptRecord: Prop.WriteOneLevel(aWriter,Value,Options); ptCustom: begin TJSONCustomParserCustom(Prop).GetJSONCustomParserRegistration. Writer(aWriter,Value^); Inc(Value,Prop.fDataSize); end; end; aWriter.Add(','); inc(Value,JSONRTTI_SIZE[Prop.PropertyType]); end; var i: integer; SubProp: TJSONCustomParserRTTI; begin if P=nil then begin aWriter.AddShort('null'); exit; |
> > > > > < < < < < < | | > > < | | > > > < | |
21834 21835 21836 21837 21838 21839 21840 21841 21842 21843 21844 21845 21846 21847 21848 21849 21850 21851 21852 21853 21854 21855 21856 21857 21858 21859 ..... 21879 21880 21881 21882 21883 21884 21885 21886 21887 21888 21889 21890 21891 21892 21893 21894 21895 ..... 21934 21935 21936 21937 21938 21939 21940 21941 21942 21943 21944 21945 21946 21947 21948 21949 ..... 21984 21985 21986 21987 21988 21989 21990 21991 21992 21993 21994 21995 21996 21997 ..... 22013 22014 22015 22016 22017 22018 22019 22020 22021 22022 22023 22024 22025 22026 22027 ..... 22136 22137 22138 22139 22140 22141 22142 22143 22144 22145 22146 22147 22148 22149 22150 22151 22152 22153 22154 22155 22156 22157 22158 22159 22160 22161 |
raise ESynException.CreateFmt('"%s" type should not have been un-registered', [fRecordTypeName]); end; { TJSONCustomParserRTTI } /// if defined, will try to mimic the default record alignment // -> is buggy, and compiler revision specific -> we would rather use packed records {.$define ALIGNCUSTOMREC} procedure TJSONCustomParserRTTI.ComputeDataSizeAfterAdd; const // binary size (in bytes) of each kind of property - 0 for ptRecord/ptCustom JSONRTTI_SIZE: array[TJSONCustomParserRTTIType] of byte = ( SizeOf(PtrUInt),SizeOf(Boolean),SizeOf(Byte),SizeOf(Cardinal),SizeOf(Currency), SizeOf(Double),SizeOf(Int64),SizeOf(Integer),SizeOf(RawUTF8),0,SizeOf(Single), SizeOf(String),SizeOf(SynUnicode),SizeOf(TDateTime),SizeOf(TGUID), SizeOf(TTimeLog),SizeOf(WideString),SizeOf(Word),0); var i: integer; begin if fDataSize=0 then begin assert(fFullDataSize=0); for i := 0 to high(NestedProperty) do begin NestedProperty[i].ComputeDataSizeAfterAdd; inc(fFullDataSize,NestedProperty[i].fDataSize); ................................................................................ case NestedProperty[j].PropertyType of ptRawUTF8: PRawUTF8(Data)^ := ''; ptString: PString(Data)^ := ''; ptSynUnicode: PSynUnicode(Data)^ := ''; ptWideString: PWideString(Data)^ := ''; ptArray: NestedProperty[j].FinalizeNestedArray(PPtrUInt(Data)^); ptRecord: NestedProperty[j].FinalizeNestedRecord(Data); ptCustom: RecordClear(Data^,TJSONCustomParserCustom(NestedProperty[j]).fRecordTypeInfo); end; inc(Data,NestedProperty[j].fDataSize); end; end; procedure TJSONCustomParserRTTI.FinalizeNestedArray(var Data: PtrUInt); var i: integer; Rec: ^TDynArrayRec; ItemData: PByte; ................................................................................ case Prop.PropertyType of ptRecord: begin if not Prop.ReadOneLevel(P,Data,Options) then exit; EndOfObject := P^; if P^ in [',','}'] then inc(P); result := true; exit; end; ptArray: begin if P^<>'[' then exit; // we expect a true array here repeat inc(P) until P^<>' '; // allocate nested array at once ArrayLen := JSONArrayCount(P); ................................................................................ end; end; ptCustom: begin P := TJSONCustomParserCustom(Prop).GetJSONCustomParserRegistration. Reader(P,Data^,valid); if (P=nil) or not valid then exit; EndOfObject := P^; if P^ in [',','}'] then inc(P); end; else begin PropValue := GetJSONField(P,P,@wasString,@EndOfObject); if (PropValue=nil) or (wasString<>(Prop.PropertyType in ................................................................................ ptTimeLog: PInt64(Data)^ := Iso8601ToSecondsPUTF8Char(PropValue,0); ptWideString:UTF8ToWideString(PropValue,StrLen(PropValue),PWideString(Data)^); ptWord: PWord(Data)^ := GetCardinal(PropValue); ptGUID: TextToGUID(PropValue,pointer(Data)); end; end; end; inc(Data,Prop.fDataSize); result := true; end; var i,j: integer; PropName: RawUTF8; wasString: boolean; Values: array of PUTF8Char; begin ................................................................................ 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: begin TJSONCustomParserCustom(Prop).GetJSONCustomParserRegistration. Writer(aWriter,Value^); end; end; aWriter.Add(','); inc(Value,Prop.fDataSize); end; var i: integer; SubProp: TJSONCustomParserRTTI; begin if P=nil then begin aWriter.AddShort('null'); exit; |
Changes to SynSelfTests.pas.
4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 |
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2Title), __TTestCustomJSON2Title).Options := [soWriteHumanReadable]; TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2), __TTestCustomJSON2).Options := [soWriteHumanReadable]; fillchar(Trans,sizeof(Trans),0); U := RecordSaveJSON(Trans,TypeInfo(TTestCustomJSON2)); Check(U=#$D#$A'{'#$D#$A#9'"Transactions": []'#$D#$A'}'); U := '{"transactions":[{"TRTYPE":"INCOME","TRDATE":"2013-12-09 02:30:04","TRAA":"1.23",'+ '"TRCAT1":{"TITYPE":"C1","TIID":"1","TICID":"","TIDSC30":"description1","TIORDER":"0","TIDEL":"false"},'+ '"TRCAT2":{"TITYPE":"C2","TIID":"2","TICID":"","TIDSC30":"description2","TIORDER":"0","TIDEL":"false"},'+ '"TRCAT3":{"TITYPE":"C3","TIID":"3","TICID":"","TIDSC30":"description3","TIORDER":"0","TIDEL":"false"},'+ '"TRRMK":"Remark",'+ '"TRACID":{"TITYPE":"AC","TIID":"4","TICID":"","TIDSC30":"account1","TIORDER":"0","TIDEL":"false"}}]}'; RecordLoadJSON(Trans,@U[1],TypeInfo(TTestCustomJSON2)); Check(length(Trans.Transactions)=1); Check(Trans.Transactions[0].TRTYPE='INCOME'); Check(Trans.Transactions[0].TRACID.TIDEL='false'); Check(Trans.Transactions[0].TRRMK='Remark'); U := RecordSaveJSON(Trans,TypeInfo(TTestCustomJSON2)); FileFromString(U,'transactions.json'); Check(Hash32(U)=$7F799CD9); TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2Title),''); TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2),''); Parser := TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomDiscogs), __TTestCustomDiscogs) as TJSONCustomParserFromTextDefinition; Parser.Options := [soReadIgnoreUnknownFields]; fillchar(Disco,sizeof(Disco),0); |
> | | | | | | | | | | | | | | > |
4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 |
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2Title), __TTestCustomJSON2Title).Options := [soWriteHumanReadable]; TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2), __TTestCustomJSON2).Options := [soWriteHumanReadable]; fillchar(Trans,sizeof(Trans),0); U := RecordSaveJSON(Trans,TypeInfo(TTestCustomJSON2)); Check(U=#$D#$A'{'#$D#$A#9'"Transactions": []'#$D#$A'}'); for i := 1 to 10 do begin U := '{"transactions":[{"TRTYPE":"INCOME","TRDATE":"2013-12-09 02:30:04","TRAA":"1.23",'+ '"TRCAT1":{"TITYPE":"C1","TIID":"1","TICID":"","TIDSC30":"description1","TIORDER":"0","TIDEL":"false"},'+ '"TRCAT2":{"TITYPE":"C2","TIID":"2","TICID":"","TIDSC30":"description2","TIORDER":"0","TIDEL":"false"},'+ '"TRCAT3":{"TITYPE":"C3","TIID":"3","TICID":"","TIDSC30":"description3","TIORDER":"0","TIDEL":"false"},'+ '"TRRMK":"Remark",'+ '"TRACID":{"TITYPE":"AC","TIID":"4","TICID":"","TIDSC30":"account1","TIORDER":"0","TIDEL":"false"}}]}'; RecordLoadJSON(Trans,@U[1],TypeInfo(TTestCustomJSON2)); Check(length(Trans.Transactions)=1); Check(Trans.Transactions[0].TRTYPE='INCOME'); Check(Trans.Transactions[0].TRACID.TIDEL='false'); Check(Trans.Transactions[0].TRRMK='Remark'); U := RecordSaveJSON(Trans,TypeInfo(TTestCustomJSON2)); FileFromString(U,'transactions.json'); Check(Hash32(U)=$7F799CD9); end; TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2Title),''); TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomJSON2),''); Parser := TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TTestCustomDiscogs), __TTestCustomDiscogs) as TJSONCustomParserFromTextDefinition; Parser.Options := [soReadIgnoreUnknownFields]; fillchar(Disco,sizeof(Disco),0); |