Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | implement woHumanReadableEnumSetAsComment option for JSON serialization (and corresponding TEnumType.GetEnumNameTrimedAll() method) - also includes regresion tests for all human readable JSON format features |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
64320a3a9870c647f4e224c81a24f903 |
User & Date: | User 2013-11-10 13:03:04 |
2013-11-10
| ||
13:15 | enhanced PostgreSQL support via ZDBC, and fix error when using schema at index creation check-in: bbe911d7d2 user: User tags: trunk | |
13:03 | implement woHumanReadableEnumSetAsComment option for JSON serialization (and corresponding TEnumType.GetEnumNameTrimedAll() method) - also includes regresion tests for all human readable JSON format features check-in: 64320a3a98 user: User tags: trunk | |
2013-11-09
| ||
22:43 | TSQLDBStatement.FetchAllToJSON will now add column names (in non-expanded JSON format) if no data row is returned - just like TSQLRequest.Execute check-in: 44ef602e2c user: User tags: trunk | |
Changes to SQLite3/mORMot.pas.
760 761 762 763 764 765 766 767 768 769 770 771 772 773 .... 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 ..... 18786 18787 18788 18789 18790 18791 18792 18793 18794 18795 18796 18797 18798 18799 ..... 29858 29859 29860 29861 29862 29863 29864 29865 29866 29867 29868 29869 29870 29871 29872 29873 29874 29875 ..... 30015 30016 30017 30018 30019 30020 30021 30022 30023 30024 30025 30026 30027 30028 30029 30030 30031 30032 30033 30034 30035 30036 ..... 30038 30039 30040 30041 30042 30043 30044 30045 30046 30047 30048 30049 30050 30051 |
- added TAuthSession.SentHeaders, RemoteIP and ConnectionID properties - added process of Variant and WideString types in TSQLRecord properties - added JSON serialization of Variant and WideString types in JSONToObject() / ObjectToJSON() functions and WriteObject method - added TTypeInfo.ClassCreate() method to create a TObject instance from RTTI - TEnumType.GetEnumNameValue() will now recognize both 'sllWarning' and 'Warning' text as a sllWarning item (will enhance JSONToObject() process) - added ClassInstanceCreate() function calling any known virtual constructor - added TPropInfo.ClassFromJSON() to properly unserialize TObject properties - added TSQLPropInfo.SQLFieldTypeName property - fixed [f96cf0fc5d] and [221ee9c767] about TSQLRecordMany JSON serialization - fixed issue when retrieving a TSQLRecord containing TSQLRecordMany properties with external tables (like 'no such column DestList' error) via SQLite3 virtual tables (e.g. for a JOINed query like FillPrepareMany) ................................................................................ function GetCaptionStrings(UsedValuesBits: Pointer=nil): string; /// add caption names, ready to be display, to a TStrings class // - add pointer(ord(element)) as Objects[] value // - if UsedValuesBits is not nil, only the corresponding bits set are added // - can be used e.g. to populate a combo box as such: // ! PTypeInfo(TypeInfo(TMyEnum))^.EnumBaseType^.AddCaptionStrings(ComboBox.Items); procedure AddCaptionStrings(Strings: TStrings; UsedValuesBits: Pointer=nil); /// get the corresponding enumeration ordinal value, from its name without // its first lowercase chars ('Done' will find otDone e.g.) // - return -1 if not found (don't use directly this value to avoid any GPF) function GetEnumNameTrimedValue(const EnumName: ShortString): Integer; overload; /// get the corresponding enumeration ordinal value, from its name without // its first lowercase chars ('Done' will find otDone e.g.) // - return -1 if not found (don't use directly this value to avoid any GPF) ................................................................................ {$endif} function TEnumType.GetCaption(const Value): string; // GetCaptionFromPCharLen() expect ASCIIz -> use temp RawUTF8 begin GetCaptionFromPCharLen(pointer(GetEnumNameTrimed(Value)),result); end; procedure TEnumType.AddCaptionStrings(Strings: TStrings; UsedValuesBits: Pointer=nil); var i, L: integer; Line: array[byte] of AnsiChar; P: PAnsiChar; V: PShortString; s: string; ................................................................................ begin fInternalJSONWriter.Free; inherited; end; procedure TJSONSerializer.WriteObject(Value: TObject; Options: TTextWriterWriteObjectOptions); var Added: boolean; procedure HR(P: PPropInfo=nil); var i: integer; begin if woHumanReadable in Options then begin AddCR; for i := 1 to fHumanReadableLevel do Add(#9); end; if P=nil then exit; AddPropName(P^.Name); ................................................................................ if (V<>P^.Default) or not (woDontStoreDefault in Options) then begin HR(P); if {$ifdef FPC}(Kind=tkBool){$else} (Kind=tkEnumeration) and (P^.PropType^=TypeInfo(boolean)){$endif} then AddString(JSON_BOOLEAN[boolean(V)]) else if (woFullExpand in Options) or (woHumanReadable in Options) then case Kind of tkEnumeration: begin Add('"'); AddTrimLeftLowerCase(P^.PropType^^.EnumBaseType^.GetEnumNameOrd(V)); Add('"'); end; tkSet: begin Add('['); with P^.PropType^^.SetEnumType^ do if (woHumanReadableFullSetsAsStar in Options) and (MaxValue<32) and GetAllBits(V,MaxValue+1) then AddShort('"*"') else begin PS := @NameList; for j := MinValue to MaxValue do begin if GetBit(V,j) then begin Add('"'); ................................................................................ Add('"',','); end; inc(PtrUInt(PS),ord(PS^[0])+1); // next item end; end; CancelLastComma; Add(']'); end; else Add(V); end else Add(V); // typecast enums and sets as plain integer by default end; end; |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > < > > |
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 .... 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 ..... 18790 18791 18792 18793 18794 18795 18796 18797 18798 18799 18800 18801 18802 18803 18804 18805 18806 18807 18808 18809 18810 18811 18812 18813 18814 18815 18816 18817 18818 18819 18820 18821 18822 ..... 29881 29882 29883 29884 29885 29886 29887 29888 29889 29890 29891 29892 29893 29894 29895 29896 29897 29898 29899 29900 29901 29902 29903 29904 ..... 30044 30045 30046 30047 30048 30049 30050 30051 30052 30053 30054 30055 30056 30057 30058 30059 30060 30061 30062 30063 30064 30065 30066 30067 30068 ..... 30070 30071 30072 30073 30074 30075 30076 30077 30078 30079 30080 30081 30082 30083 30084 30085 |
- added TAuthSession.SentHeaders, RemoteIP and ConnectionID properties - added process of Variant and WideString types in TSQLRecord properties - added JSON serialization of Variant and WideString types in JSONToObject() / ObjectToJSON() functions and WriteObject method - added TTypeInfo.ClassCreate() method to create a TObject instance from RTTI - TEnumType.GetEnumNameValue() will now recognize both 'sllWarning' and 'Warning' text as a sllWarning item (will enhance JSONToObject() process) - implement woHumanReadableFullSetsAsStar and woHumanReadableEnumSetAsComment option for JSON serialization and TEnumType.GetEnumNameTrimedAll() - added ClassInstanceCreate() function calling any known virtual constructor - added TPropInfo.ClassFromJSON() to properly unserialize TObject properties - added TSQLPropInfo.SQLFieldTypeName property - fixed [f96cf0fc5d] and [221ee9c767] about TSQLRecordMany JSON serialization - fixed issue when retrieving a TSQLRecord containing TSQLRecordMany properties with external tables (like 'no such column DestList' error) via SQLite3 virtual tables (e.g. for a JOINed query like FillPrepareMany) ................................................................................ function GetCaptionStrings(UsedValuesBits: Pointer=nil): string; /// add caption names, ready to be display, to a TStrings class // - add pointer(ord(element)) as Objects[] value // - if UsedValuesBits is not nil, only the corresponding bits set are added // - can be used e.g. to populate a combo box as such: // ! PTypeInfo(TypeInfo(TMyEnum))^.EnumBaseType^.AddCaptionStrings(ComboBox.Items); procedure AddCaptionStrings(Strings: TStrings; UsedValuesBits: Pointer=nil); /// retrieve all trimed element names as CSV procedure GetEnumNameTrimedAll(var result: RawUTF8); /// get the corresponding enumeration ordinal value, from its name without // its first lowercase chars ('Done' will find otDone e.g.) // - return -1 if not found (don't use directly this value to avoid any GPF) function GetEnumNameTrimedValue(const EnumName: ShortString): Integer; overload; /// get the corresponding enumeration ordinal value, from its name without // its first lowercase chars ('Done' will find otDone e.g.) // - return -1 if not found (don't use directly this value to avoid any GPF) ................................................................................ {$endif} function TEnumType.GetCaption(const Value): string; // GetCaptionFromPCharLen() expect ASCIIz -> use temp RawUTF8 begin GetCaptionFromPCharLen(pointer(GetEnumNameTrimed(Value)),result); end; procedure TEnumType.GetEnumNameTrimedAll(var result: RawUTF8); var i: integer; V: PShortString; begin with TTextWriter.CreateOwnedStream(1024) do try V := @NameList; for i := MinValue to MaxValue do begin AddTrimLeftLowerCase(V); Add(','); inc(PtrUInt(V),length(V^)+1); end; CancelLastComma; SetText(result); finally Free; end; end; procedure TEnumType.AddCaptionStrings(Strings: TStrings; UsedValuesBits: Pointer=nil); var i, L: integer; Line: array[byte] of AnsiChar; P: PAnsiChar; V: PShortString; s: string; ................................................................................ begin fInternalJSONWriter.Free; inherited; end; procedure TJSONSerializer.WriteObject(Value: TObject; Options: TTextWriterWriteObjectOptions); var Added: boolean; CustomComment: RawUTF8; procedure HR(P: PPropInfo=nil); var i: integer; begin if woHumanReadable in Options then begin if CustomComment<>'' then begin AddShort(' // '); AddString(CustomComment); CustomComment := ''; end; AddCR; for i := 1 to fHumanReadableLevel do Add(#9); end; if P=nil then exit; AddPropName(P^.Name); ................................................................................ if (V<>P^.Default) or not (woDontStoreDefault in Options) then begin HR(P); if {$ifdef FPC}(Kind=tkBool){$else} (Kind=tkEnumeration) and (P^.PropType^=TypeInfo(boolean)){$endif} then AddString(JSON_BOOLEAN[boolean(V)]) else if (woFullExpand in Options) or (woHumanReadable in Options) then case Kind of tkEnumeration: with P^.PropType^^.EnumBaseType^ do begin Add('"'); AddTrimLeftLowerCase(GetEnumNameOrd(V)); Add('"'); if woHumanReadableEnumSetAsComment in Options then GetEnumNameTrimedAll(CustomComment); end; tkSet: with P^.PropType^^.SetEnumType^ do begin Add('['); if (woHumanReadableFullSetsAsStar in Options) and (MaxValue<32) and GetAllBits(V,MaxValue+1) then AddShort('"*"') else begin PS := @NameList; for j := MinValue to MaxValue do begin if GetBit(V,j) then begin Add('"'); ................................................................................ Add('"',','); end; inc(PtrUInt(PS),ord(PS^[0])+1); // next item end; end; CancelLastComma; Add(']'); if woHumanReadableEnumSetAsComment in Options then GetEnumNameTrimedAll(CustomComment); end; else Add(V); end else Add(V); // typecast enums and sets as plain integer by default end; end; |
Changes to SynCommons.pas.
3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 |
// avoid serializing properties including a default value (JSONToObject function // will set the default values, so it may help saving some bandwidth or storage) // - woFullExpand will generate a log-friendly layout, including instance class // name and reference pointer (it is used in TSynLog) // - woStoreClassName will add a "ClassName":"TMyClass", field // - woHumanReadableFullSetsAsStar will store an human-readable set with // all its enumerates items set to be stored as ["*"] TTextWriterWriteObjectOption = ( woHumanReadable, woDontStoreDefault, woFullExpand, woStoreClassName, woHumanReadableFullSetsAsStar); /// options set for TTextWriter.WriteObject() method TTextWriterWriteObjectOptions = set of TTextWriterWriteObjectOption; /// simple writer to a Stream, specialized for the TEXT format // - use an internal buffer, faster than string+string // - some dedicated methods is able to encode any data with JSON escape TTextWriter = class |
> > > | |
3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 |
// avoid serializing properties including a default value (JSONToObject function // will set the default values, so it may help saving some bandwidth or storage) // - woFullExpand will generate a log-friendly layout, including instance class // name and reference pointer (it is used in TSynLog) // - woStoreClassName will add a "ClassName":"TMyClass", field // - woHumanReadableFullSetsAsStar will store an human-readable set with // all its enumerates items set to be stored as ["*"] // - woHumanReadableEnumSetAsComment will add a comment at the end of the // line, containing all available values of the enumaration or set, e.g: // $ "Enum": "Destroying", // Idle,Started,Finished,Destroying TTextWriterWriteObjectOption = ( woHumanReadable, woDontStoreDefault, woFullExpand, woStoreClassName, woHumanReadableFullSetsAsStar, woHumanReadableEnumSetAsComment); /// options set for TTextWriter.WriteObject() method TTextWriterWriteObjectOptions = set of TTextWriterWriteObjectOption; /// simple writer to a Stream, specialized for the TEXT format // - use an internal buffer, faster than string+string // - some dedicated methods is able to encode any data with JSON escape TTextWriter = class |
Changes to SynSelfTests.pas.
3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 .... 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 .... 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 |
fColl := Value; end; {$endif FPC} {$endif DELPHI5OROLDER} {$endif LVCL} procedure TTestLowLevelTypes.EncodeDecodeJSON; var J,U: RawUTF8; V: TPUtf8CharDynArray; i, a, err: integer; r: Double; {$ifndef DELPHI5OROLDER} ................................................................................ {$endif} {$endif} {$endif} {$ifndef LVCL} {$ifndef DELPHI5OROLDER} var P: PUTF8Char; {$endif} {$endif} {$ifdef USEVARIANTS} var Va: Variant; c: currency; {$endif} begin ................................................................................ J := GetJSONObjectAsSQL(J,false,true); Check(J=U); J := '{"RowID": 210 ,"Name":"Alice","Role":"User","Last Login":null, // comment'#13#10+ '"First Login" : /* to be ignored */ null , "Department" : "{\"relPath\":\"317\\\\\",\"revision\":1}" } ]'; RemoveCommentsFromJSON(@J[1]); J := GetJSONObjectAsSQL(J,false,true,1,True); Check(J=U); {$ifndef LVCL} C2 := TCollTst.Create; Coll := TCollTst.Create; try U := ObjectToJSON(Coll); Check(Hash32(U)=$95B54414); Check(ObjectToJSON(C2)=U); |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 .... 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 .... 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 |
fColl := Value; end; {$endif FPC} {$endif DELPHI5OROLDER} {$endif LVCL} type TPersistentToJSON = class(TPersistent) protected fName: RawUTF8; fEnum: TSynBackgroundThreadProcessStep; fSets: TSynBackgroundThreadProcessSteps; published property Name: RawUTF8 read fName; property Enum: TSynBackgroundThreadProcessStep read fEnum default flagIdle; property Sets: TSynBackgroundThreadProcessSteps read fSets default []; end; procedure TTestLowLevelTypes.EncodeDecodeJSON; var J,U: RawUTF8; V: TPUtf8CharDynArray; i, a, err: integer; r: Double; {$ifndef DELPHI5OROLDER} ................................................................................ {$endif} {$endif} {$endif} {$ifndef LVCL} {$ifndef DELPHI5OROLDER} var P: PUTF8Char; O,O2: TPersistentToJSON; E: TSynBackgroundThreadProcessStep; {$endif} {$endif} {$ifdef USEVARIANTS} var Va: Variant; c: currency; {$endif} begin ................................................................................ J := GetJSONObjectAsSQL(J,false,true); Check(J=U); J := '{"RowID": 210 ,"Name":"Alice","Role":"User","Last Login":null, // comment'#13#10+ '"First Login" : /* to be ignored */ null , "Department" : "{\"relPath\":\"317\\\\\",\"revision\":1}" } ]'; RemoveCommentsFromJSON(@J[1]); J := GetJSONObjectAsSQL(J,false,true,1,True); Check(J=U); O := TPersistentToJSON.Create; O2 := TPersistentToJSON.Create; try J := ObjectToJSON(O,[]); Check(J='{"Name":"","Enum":0,"Sets":0}'); J := ObjectToJSON(O,[woDontStoreDefault]); Check(J='{"Name":""}'); J := ObjectToJSON(O,[woStoreClassName]); Check(J='{"ClassName":"TPersistentToJSON","Name":"","Enum":0,"Sets":0}'); J := ObjectToJSON(O,[woHumanReadable]); Check(J='{'#$D#$A#9'"Name": "",'#$D#$A#9'"Enum": "Idle",'#$D#$A#9'"Sets": []'#$D#$A'}'); with PTypeInfo(TypeInfo(TSynBackgroundThreadProcessStep))^.EnumBaseType^ do for E := low(E) to high(E) do begin O.fName := Int32ToUTF8(ord(E)); O.fEnum := E; include(O.fSets,E); J := ObjectToJSON(O,[]); Check(J=FormatUTF8('{"Name":"%","Enum":%,"Sets":%}',[ord(E),ord(E),byte(O.fSets)])); JSONToObject(O2,pointer(J),valid); Check(Valid); Check(O.Name=O2.Name); Check(O.Enum=O2.Enum); Check(O.Sets=O2.Sets); J := ObjectToJSON(O,[woHumanReadable]); U := FormatUTF8( '{'#$D#$A#9'"NAME": "%",'#$D#$A#9'"ENUM": "%",'#$D#$A#9'"SETS": ["IDLE"', [ord(E),UpperCaseU(GetEnumNameTrimed(E))]); Check(IdemPChar(pointer(J),pointer(U))); JSONToObject(O2,pointer(J),valid); Check(Valid); Check(O.Name=O2.Name); Check(O.Enum=O2.Enum); Check(O.Sets=O2.Sets); end; J := ObjectToJSON(O,[woHumanReadable,woHumanReadableFullSetsAsStar]); Check(J='{'#$D#$A#9'"Name": "3",'#$D#$A#9'"Enum": "Destroying",'#$D#$A#9'"Sets": ["*"]'#$D#$A'}'); J := ObjectToJSON(O,[woHumanReadable,woHumanReadableFullSetsAsStar,woHumanReadableEnumSetAsComment]); Check(J='{'#$D#$A#9'"Name": "3",'#$D#$A#9'"Enum": "Destroying", // Idle,Started,Finished,Destroying'+ #$D#$A#9'"Sets": ["*"] // Idle,Started,Finished,Destroying'#$D#$A'}'); O2.fName := ''; O2.fEnum := low(E); O2.fSets := []; RemoveCommentsFromJSON(@J[1]); JSONToObject(O2,pointer(J),valid); Check(Valid); Check(O.Name=O2.Name); Check(O.Enum=O2.Enum); Check(O.Sets=O2.Sets); finally O2.Free; O.Free; end; {$ifndef LVCL} C2 := TCollTst.Create; Coll := TCollTst.Create; try U := ObjectToJSON(Coll); Check(Hash32(U)=$95B54414); Check(ObjectToJSON(C2)=U); |