Neon.Core.Persistence.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. {******************************************************************************}
  2. { }
  3. { Neon: Serialization Library for Delphi }
  4. { Copyright (c) 2018-2019 Paolo Rossi }
  5. { https://github.com/paolo-rossi/neon-library }
  6. { }
  7. {******************************************************************************}
  8. { }
  9. { Licensed under the Apache License, Version 2.0 (the "License"); }
  10. { you may not use this file except in compliance with the License. }
  11. { You may obtain a copy of the License at }
  12. { }
  13. { http://www.apache.org/licenses/LICENSE-2.0 }
  14. { }
  15. { Unless required by applicable law or agreed to in writing, software }
  16. { distributed under the License is distributed on an "AS IS" BASIS, }
  17. { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
  18. { See the License for the specific language governing permissions and }
  19. { limitations under the License. }
  20. { }
  21. {******************************************************************************}
  22. unit Neon.Core.Persistence;
  23. interface
  24. uses
  25. System.SysUtils, System.Classes, System.Rtti, System.SyncObjs, System.TypInfo,
  26. System.Generics.Collections, System.JSON, System.Generics.Defaults,
  27. Neon.Core.Types,
  28. Neon.Core.Attributes,
  29. Neon.Core.DynamicTypes;
  30. {$SCOPEDENUMS ON}
  31. type
  32. TNeonSerializerRegistry = class;
  33. TNeonRttiObject = class;
  34. INeonConfiguration = interface
  35. ['{F82AB790-1C65-4501-915C-0289EFD9D8CC}']
  36. function SetMembers(AValue: TNeonMembersSet): INeonConfiguration;
  37. function SetMemberCase(AValue: TNeonCase): INeonConfiguration;
  38. function SetMemberCustomCase(AValue: TCaseFunc): INeonConfiguration;
  39. function SetVisibility(AValue: TNeonVisibility): INeonConfiguration;
  40. function SetIgnoreFieldPrefix(AValue: Boolean): INeonConfiguration;
  41. function SetUseUTCDate(AValue: Boolean): INeonConfiguration;
  42. function SetPrettyPrint(AValue: Boolean): INeonConfiguration;
  43. function GetPrettyPrint: Boolean;
  44. function GetUseUTCDate: Boolean;
  45. function GetSerializers: TNeonSerializerRegistry;
  46. end;
  47. IConfigurationContext = interface
  48. ['{3954FFB5-2D3D-4978-AADA-FEC5C0D73FD0}']
  49. function GetConfiguration: INeonConfiguration;
  50. end;
  51. ISerializerContext = interface(IConfigurationContext)
  52. ['{36A014FC-9E3F-4EBF-9545-CF9DBCBF507C}']
  53. function WriteDataMember(const AValue: TValue): TJSONValue;
  54. end;
  55. IDeserializerContext = interface(IConfigurationContext)
  56. ['{5351D1F9-99B3-4826-B981-4CBF926085D6}']
  57. function ReadDataMember(AJSONValue: TJSONValue; AType: TRttiType; const AData: TValue): TValue;
  58. end;
  59. TCustomSerializer = class abstract(TObject)
  60. protected
  61. class function GetTargetInfo: PTypeInfo; virtual;
  62. class function CanHandle(AType: PTypeInfo): Boolean; virtual; abstract;
  63. protected
  64. class function ClassDistance: Integer;
  65. class function ClassIs(AClass: TClass): Boolean;
  66. class function TypeInfoIs(AInfo: PTypeInfo): Boolean;
  67. class function TypeInfoIsClass(AInfo: PTypeInfo): Boolean;
  68. public
  69. function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; virtual; abstract;
  70. function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; virtual; abstract;
  71. end;
  72. TCustomSerializerClass = class of TCustomSerializer;
  73. TSerializerInfo = record
  74. public
  75. SerializerClass: TCustomSerializerClass;
  76. Distance: Integer;
  77. public
  78. class function FromSerializer(ASerializerClass: TCustomSerializerClass): TSerializerInfo; static;
  79. end;
  80. TNeonSerializerRegistry = class
  81. private type
  82. SerializerCacheRegistry = class(TObjectDictionary<PTypeInfo, TCustomSerializer>);
  83. SerializerClassRegistry = class(TList<TSerializerInfo>);
  84. private
  85. FRegistryClass: SerializerClassRegistry;
  86. FRegistryCache: SerializerCacheRegistry;
  87. function GetCount: Integer;
  88. function InternalGetSerializer(ATypeInfo: PTypeInfo): TCustomSerializer;
  89. public
  90. constructor Create;
  91. destructor Destroy; override;
  92. public
  93. procedure Clear;
  94. procedure ClearCache;
  95. procedure Assign(ARegistry: TNeonSerializerRegistry);
  96. function RegisterSerializer(ASerializerClass: TCustomSerializerClass): TNeonSerializerRegistry; overload;
  97. procedure UnregisterSerializer(ASerializerClass: TCustomSerializerClass);
  98. function GetSerializer<T>: TCustomSerializer; overload;
  99. function GetSerializer(AValue: TValue): TCustomSerializer; overload;
  100. function GetSerializer(ATargetClass: TClass): TCustomSerializer; overload;
  101. function GetSerializer(ATargetInfo: PTypeInfo): TCustomSerializer; overload;
  102. public
  103. property Count: Integer read GetCount;
  104. end;
  105. TCaseAlgorithm = class
  106. public
  107. class function PascalToCamel(const AString: string): string;
  108. class function CamelToPascal(const AString: string): string;
  109. class function PascalToSnake(const AString: string): string;
  110. class function SnakeToPascal(const AString: string): string;
  111. end;
  112. TNeonConfiguration = class sealed(TInterfacedObject, INeonConfiguration)
  113. private
  114. FVisibility: TNeonVisibility;
  115. FMembers: TNeonMembersSet;
  116. FMemberCase: TNeonCase;
  117. FMemberCustomCase: TCaseFunc;
  118. FIgnoreFieldPrefix: Boolean;
  119. FUseUTCDate: Boolean;
  120. FPrettyPrint: Boolean;
  121. FSerializers: TNeonSerializerRegistry;
  122. public
  123. constructor Create;
  124. destructor Destroy; override;
  125. class function Default: INeonConfiguration; static;
  126. class function Pretty: INeonConfiguration; static;
  127. class function Snake: INeonConfiguration; static;
  128. class function Camel: INeonConfiguration; static;
  129. function SetMembers(AValue: TNeonMembersSet): INeonConfiguration;
  130. function SetMemberCase(AValue: TNeonCase): INeonConfiguration;
  131. function SetMemberCustomCase(AValue: TCaseFunc): INeonConfiguration;
  132. function SetVisibility(AValue: TNeonVisibility): INeonConfiguration;
  133. function SetIgnoreFieldPrefix(AValue: Boolean): INeonConfiguration;
  134. function SetUseUTCDate(AValue: Boolean): INeonConfiguration;
  135. function SetPrettyPrint(AValue: Boolean): INeonConfiguration;
  136. function GetUseUTCDate: Boolean;
  137. function GetPrettyPrint: Boolean;
  138. function GetSerializers: TNeonSerializerRegistry;
  139. property Members: TNeonMembersSet read FMembers write FMembers;
  140. property MemberCase: TNeonCase read FMemberCase write FMemberCase;
  141. property MemberCustomCase: TCaseFunc read FMemberCustomCase write FMemberCustomCase;
  142. property Visibility: TNeonVisibility read FVisibility write FVisibility;
  143. property IgnoreFieldPrefix: Boolean read FIgnoreFieldPrefix write FIgnoreFieldPrefix;
  144. property UseUTCDate: Boolean read FUseUTCDate write FUseUTCDate;
  145. property Serializers: TNeonSerializerRegistry read FSerializers write FSerializers;
  146. end;
  147. TNeonRttiObject = class
  148. protected
  149. FOperation: TNeonOperation;
  150. FRttiObject: TRttiObject;
  151. FNeonInclude: TIncludeValue;
  152. FAttributes: TArray<TCustomAttribute>;
  153. FNeonMembers: TNeonMembersSet;
  154. FNeonVisibility: TNeonVisibility;
  155. FNeonIgnore: Boolean;
  156. FNeonProperty: string;
  157. FNeonEnumNames: TArray<string>;
  158. FNeonSerializerName: string;
  159. FNeonSerializerClass: TClass;
  160. private
  161. FTypeAttributes: TArray<TCustomAttribute>;
  162. protected
  163. procedure InternalParseAttributes(const AAttr: TArray<TCustomAttribute>); virtual;
  164. procedure ProcessAttribute(AAttribute: TCustomAttribute); virtual;
  165. function AsRttiType: TRttiType;
  166. public
  167. constructor Create(ARttiObject: TRttiObject; AOperation: TNeonOperation);
  168. public
  169. procedure ParseAttributes; virtual;
  170. property Attributes: TArray<TCustomAttribute> read FAttributes write FAttributes;
  171. property TypeAttributes: TArray<TCustomAttribute> read FTypeAttributes write FTypeAttributes;
  172. // Neon-based properties
  173. property NeonIgnore: Boolean read FNeonIgnore write FNeonIgnore;
  174. property NeonInclude: TIncludeValue read FNeonInclude write FNeonInclude;
  175. property NeonSerializerName: string read FNeonSerializerName write FNeonSerializerName;
  176. property NeonSerializerClass: TClass read FNeonSerializerClass write FNeonSerializerClass;
  177. property NeonProperty: string read FNeonProperty write FNeonProperty;
  178. property NeonEnumNames: TArray<string> read FNeonEnumNames write FNeonEnumNames;
  179. property NeonMembers: TNeonMembersSet read FNeonMembers write FNeonMembers;
  180. property NeonVisibility: TNeonVisibility read FNeonVisibility write FNeonVisibility;
  181. end;
  182. TNeonRttiType = class(TNeonRttiObject)
  183. private
  184. FType: TRttiType;
  185. FInstance: Pointer;
  186. public
  187. constructor Create(AInstance: Pointer; AType: TRttiType; AOperation: TNeonOperation);
  188. property Instance: Pointer read FInstance write FInstance;
  189. end;
  190. TNeonRttiMember = class(TNeonRttiObject)
  191. private
  192. FMemberType: TNeonMemberType;
  193. FMemberRttiType: TRttiType;
  194. FMember: TRttiMember;
  195. FParent: TNeonRttiType;
  196. FSerializable: Boolean;
  197. function MemberAsProperty: TRttiProperty; inline;
  198. function MemberAsField: TRttiField; inline;
  199. function GetName: string;
  200. protected
  201. FNeonIncludeIf: TNeonIncludeOption;
  202. procedure ProcessAttribute(AAttribute: TCustomAttribute); override;
  203. public
  204. constructor Create(AMember: TRttiMember; AParent: TNeonRttiType; AOperation: TNeonOperation);
  205. function GetValue: TValue;
  206. procedure SetValue(const AValue: TValue);
  207. function RttiType: TRttiType;
  208. function MemberType: TNeonMemberType;
  209. function IsWritable: Boolean;
  210. function IsReadable: Boolean;
  211. function TypeKind: TTypeKind;
  212. function Visibility: TMemberVisibility;
  213. function IsField: Boolean;
  214. function IsProperty: Boolean;
  215. property Name: string read GetName;
  216. property NeonIncludeIf: TNeonIncludeOption read FNeonIncludeIf write FNeonIncludeIf;
  217. property Serializable: Boolean read FSerializable write FSerializable;
  218. end;
  219. TNeonRttiMembers = class(TObjectList<TNeonRttiMember>)
  220. private
  221. FOperation: TNeonOperation;
  222. FConfig: TNeonConfiguration;
  223. FInstance: Pointer;
  224. FParent: TNeonRttiType;
  225. private
  226. function MatchesVisibility(AVisibility: TMemberVisibility): Boolean;
  227. function MatchesMemberChoice(AMemberType: TNeonMemberType): Boolean;
  228. public
  229. constructor Create(AConfig: TNeonConfiguration; AInstance: Pointer; AType: TRttiType; AOperation: TNeonOperation);
  230. destructor Destroy; override;
  231. function NewMember(AMember: TRttiMember): TNeonRttiMember;
  232. procedure FilterSerialize;
  233. procedure FilterDeserialize;
  234. end;
  235. TNeonBase = class(TSingletonImplementation, IConfigurationContext)
  236. protected
  237. FConfig: TNeonConfiguration;
  238. FOperation: TNeonOperation;
  239. FOriginalInstance: TValue;
  240. FErrors: TStrings;
  241. function IsOriginalInstance(const AValue: TValue): Boolean;
  242. function GetTypeMembers(AType: TRttiType): TArray<TRttiMember>;
  243. function GetNeonMembers(AInstance: Pointer; AType: TRttiType): TNeonRttiMembers;
  244. function GetNameFromMember(AMember: TNeonRttiMember): string; virtual;
  245. public
  246. constructor Create(const AConfig: INeonConfiguration);
  247. destructor Destroy; override;
  248. procedure LogError(const AMessage: string);
  249. function GetConfiguration: INeonConfiguration;
  250. public
  251. property Config: TNeonConfiguration read FConfig write FConfig;
  252. property Errors: TStrings read FErrors write FErrors;
  253. end;
  254. implementation
  255. uses
  256. System.RegularExpressions,
  257. Neon.Core.Utils;
  258. { TNeonBase }
  259. constructor TNeonBase.Create(const AConfig: INeonConfiguration);
  260. begin
  261. FConfig := AConfig as TNeonConfiguration;
  262. FErrors := TStringList.Create;
  263. end;
  264. destructor TNeonBase.Destroy;
  265. begin
  266. FErrors.Free;
  267. inherited;
  268. end;
  269. function TNeonBase.GetConfiguration: INeonConfiguration;
  270. begin
  271. Result := FConfig;
  272. end;
  273. function TNeonBase.GetNameFromMember(AMember: TNeonRttiMember): string;
  274. var
  275. LMemberName: string;
  276. begin
  277. if not AMember.NeonProperty.IsEmpty then
  278. Exit(AMember.NeonProperty);
  279. if FConfig.IgnoreFieldPrefix and AMember.IsField then
  280. begin
  281. if AMember.Name.StartsWith('F', True) and
  282. (AMember.Visibility in [mvPrivate, mvProtected])
  283. then
  284. LMemberName := AMember.Name.Substring(1)
  285. else
  286. LMemberName := AMember.Name;
  287. end
  288. else
  289. LMemberName := AMember.Name;
  290. case FConfig.MemberCase of
  291. TNeonCase.LowerCase : Result := LowerCase(LMemberName);
  292. TNeonCase.UpperCase : Result := UpperCase(LMemberName);
  293. TNeonCase.CamelCase : Result := TCaseAlgorithm.PascalToCamel(LMemberName);
  294. TNeonCase.SnakeCase : Result := TCaseAlgorithm.PascalToSnake(LMemberName);
  295. TNeonCase.PascalCase: Result := LMemberName;
  296. TNeonCase.CustomCase: Result := FConfig.MemberCustomCase(LMemberName);
  297. end;
  298. end;
  299. function TNeonBase.GetNeonMembers(AInstance: Pointer; AType: TRttiType): TNeonRttiMembers;
  300. var
  301. LFields, LProps: TArray<TRttiMember>;
  302. LMember: TRttiMember;
  303. LNeonMember: TNeonRttiMember;
  304. begin
  305. Result := TNeonRttiMembers.Create(FConfig, AInstance, AType, FOperation);
  306. SetLength(LFields, 0);
  307. SetLength(LProps, 0);
  308. if AType.IsRecord then
  309. begin
  310. LFields := TArray<TRttiMember>(AType.AsRecord.GetFields);
  311. LProps := TArray<TRttiMember>(AType.AsRecord.GetProperties);
  312. // GetIndexedProperties
  313. end
  314. else if AType.IsInstance then
  315. begin
  316. LFields := TArray<TRttiMember>(AType.AsInstance.GetFields);
  317. LProps := TArray<TRttiMember>(AType.AsInstance.GetProperties);
  318. // GetIndexedProperties
  319. end;
  320. for LMember in LFields do
  321. begin
  322. LNeonMember := Result.NewMember(LMember);
  323. Result.Add(LNeonMember);
  324. end;
  325. for LMember in LProps do
  326. begin
  327. LNeonMember := Result.NewMember(LMember);
  328. Result.Add(LNeonMember);
  329. end;
  330. end;
  331. function TNeonBase.GetTypeMembers(AType: TRttiType): TArray<TRttiMember>;
  332. begin
  333. SetLength(Result, 0);
  334. if TNeonMembers.Standard in FConfig.Members then
  335. begin
  336. if AType.IsRecord then
  337. Result := TArray<TRttiMember>(AType.AsRecord.GetFields)
  338. else if AType.IsInstance then
  339. Result := TArray<TRttiMember>(AType.AsInstance.GetProperties);
  340. end;
  341. if TNeonMembers.Properties in FConfig.Members then
  342. begin
  343. if AType.IsRecord then
  344. Result := TArray<TRttiMember>(AType.AsRecord.GetProperties)
  345. else if AType.IsInstance then
  346. Result := TArray<TRttiMember>(AType.AsInstance.GetProperties);
  347. end;
  348. if TNeonMembers.Fields in FConfig.Members then
  349. begin
  350. if AType.IsRecord then
  351. Result := TArray<TRttiMember>(AType.AsRecord.GetFields)
  352. else if AType.IsInstance then
  353. Result := TArray<TRttiMember>(AType.AsInstance.GetFields);
  354. end;
  355. end;
  356. function TNeonBase.IsOriginalInstance(const AValue: TValue): Boolean;
  357. begin
  358. if NativeInt(AValue.GetReferenceToRawData^) = NativeInt(FOriginalInstance.GetReferenceToRawData^) then
  359. Result := True
  360. else
  361. Result := False;
  362. end;
  363. procedure TNeonBase.LogError(const AMessage: string);
  364. begin
  365. FErrors.Add(AMessage);
  366. end;
  367. { TNeonConfiguration }
  368. constructor TNeonConfiguration.Create;
  369. begin
  370. FSerializers := TNeonSerializerRegistry.Create;
  371. SetMemberCase(TNeonCase.PascalCase);
  372. SetMembers([TNeonMembers.Standard, TNeonMembers.Fields]);
  373. SetIgnoreFieldPrefix(False);
  374. SetVisibility([mvPublic, mvPublished]);
  375. SetUseUTCDate(False);
  376. SetPrettyPrint(False);
  377. end;
  378. class function TNeonConfiguration.Default: INeonConfiguration;
  379. begin
  380. Result := TNeonConfiguration.Create;
  381. end;
  382. destructor TNeonConfiguration.Destroy;
  383. begin
  384. FSerializers.Free;
  385. inherited;
  386. end;
  387. function TNeonConfiguration.GetPrettyPrint: Boolean;
  388. begin
  389. Result := FPrettyPrint;
  390. end;
  391. function TNeonConfiguration.GetSerializers: TNeonSerializerRegistry;
  392. begin
  393. Result := FSerializers;
  394. end;
  395. function TNeonConfiguration.GetUseUTCDate: Boolean;
  396. begin
  397. Result := FUseUTCDate;
  398. end;
  399. class function TNeonConfiguration.Pretty: INeonConfiguration;
  400. begin
  401. Result := TNeonConfiguration.Create;
  402. Result.SetPrettyPrint(True);
  403. end;
  404. class function TNeonConfiguration.Camel: INeonConfiguration;
  405. begin
  406. Result := TNeonConfiguration.Create;
  407. Result.SetMemberCase(TNeonCase.CamelCase);
  408. end;
  409. class function TNeonConfiguration.Snake: INeonConfiguration;
  410. begin
  411. Result := TNeonConfiguration.Create;
  412. Result.SetIgnoreFieldPrefix(True);
  413. Result.SetMemberCase(TNeonCase.SnakeCase);
  414. end;
  415. function TNeonConfiguration.SetMembers(AValue: TNeonMembersSet): INeonConfiguration;
  416. begin
  417. FMembers := AValue;
  418. Result := Self;
  419. end;
  420. function TNeonConfiguration.SetPrettyPrint(AValue: Boolean): INeonConfiguration;
  421. begin
  422. FPrettyPrint := AValue;
  423. Result := Self;
  424. end;
  425. function TNeonConfiguration.SetUseUTCDate(AValue: Boolean): INeonConfiguration;
  426. begin
  427. FUseUTCDate := AValue;
  428. Result := Self;
  429. end;
  430. function TNeonConfiguration.SetIgnoreFieldPrefix(AValue: Boolean): INeonConfiguration;
  431. begin
  432. FIgnoreFieldPrefix := AValue;
  433. Result := Self;
  434. end;
  435. function TNeonConfiguration.SetMemberCase(AValue: TNeonCase): INeonConfiguration;
  436. begin
  437. FMemberCase := AValue;
  438. Result := Self;
  439. end;
  440. function TNeonConfiguration.SetMemberCustomCase(AValue: TCaseFunc): INeonConfiguration;
  441. begin
  442. FMemberCustomCase := AValue;
  443. Result := Self;
  444. end;
  445. function TNeonConfiguration.SetVisibility(AValue: TNeonVisibility): INeonConfiguration;
  446. begin
  447. FVisibility := AValue;
  448. Result := Self;
  449. end;
  450. { TNeonRttiMember }
  451. constructor TNeonRttiMember.Create(AMember: TRttiMember; AParent: TNeonRttiType; AOperation: TNeonOperation);
  452. begin
  453. inherited Create(AMember, AOperation);
  454. FMember := AMember;
  455. FParent := AParent;
  456. if FMember is TRttiProperty then
  457. begin
  458. FMemberType := TNeonMemberType.Prop;
  459. FMemberRttiType := (FMember as TRttiProperty).PropertyType;
  460. end
  461. else if FMember is TRttiField then
  462. begin
  463. FMemberType := TNeonMemberType.Field;
  464. FMemberRttiType := (FMember as TRttiField).FieldType;
  465. end;
  466. if Assigned(FMemberRttiType) then
  467. FTypeAttributes := FMemberRttiType.GetAttributes;
  468. ParseAttributes;
  469. end;
  470. function TNeonRttiMember.GetName: string;
  471. begin
  472. Result := FMember.Name;
  473. end;
  474. function TNeonRttiMember.GetValue: TValue;
  475. begin
  476. case FMemberType of
  477. TNeonMemberType.Unknown: raise ENeonException.Create('Member type must be Field or Property');
  478. TNeonMemberType.Prop : Result := MemberAsProperty.GetValue(FParent.Instance);
  479. TNeonMemberType.Field : Result := MemberAsField.GetValue(FParent.Instance);
  480. end;
  481. end;
  482. function TNeonRttiMember.IsField: Boolean;
  483. begin
  484. Result := False;
  485. case FMemberType of
  486. TNeonMemberType.Field: Result := True;
  487. end;
  488. end;
  489. function TNeonRttiMember.IsProperty: Boolean;
  490. begin
  491. Result := False;
  492. case FMemberType of
  493. TNeonMemberType.Prop: Result := True;
  494. end;
  495. end;
  496. function TNeonRttiMember.IsReadable: Boolean;
  497. begin
  498. Result := False;
  499. case FMemberType of
  500. TNeonMemberType.Unknown: raise ENeonException.Create('Member type must be Field or Property');
  501. TNeonMemberType.Prop : Result := MemberAsProperty.IsReadable;
  502. TNeonMemberType.Field : Result := True;
  503. end;
  504. end;
  505. function TNeonRttiMember.IsWritable: Boolean;
  506. begin
  507. Result := False;
  508. case FMemberType of
  509. TNeonMemberType.Unknown: raise ENeonException.Create('Member type must be Field or Property');
  510. TNeonMemberType.Prop : Result := MemberAsProperty.IsWritable;
  511. TNeonMemberType.Field : Result := True;
  512. end;
  513. end;
  514. function TNeonRttiMember.MemberAsField: TRttiField;
  515. begin
  516. Result := FMember as TRttiField;
  517. end;
  518. function TNeonRttiMember.MemberAsProperty: TRttiProperty;
  519. begin
  520. Result := FMember as TRttiProperty;
  521. end;
  522. function TNeonRttiMember.MemberType: TNeonMemberType;
  523. begin
  524. Result := FMemberType;
  525. end;
  526. function TNeonRttiMember.RttiType: TRttiType;
  527. begin
  528. Result := nil;
  529. case FMemberType of
  530. TNeonMemberType.Unknown: raise ENeonException.Create('Member type must be Field or Property');
  531. TNeonMemberType.Prop : Result := MemberAsProperty.PropertyType;
  532. TNeonMemberType.Field : Result := MemberAsField.FieldType;
  533. end;
  534. end;
  535. procedure TNeonRttiMember.ProcessAttribute(AAttribute: TCustomAttribute);
  536. var
  537. LIncludeAttribute: NeonIncludeAttribute;
  538. LContext: TNeonIgnoreIfContext;
  539. LMethodName: string;
  540. LMethod: TRttiMethod;
  541. LRes: TValue;
  542. begin
  543. LRes := False;
  544. if AAttribute is NeonIncludeAttribute then
  545. begin
  546. LIncludeAttribute := AAttribute as NeonIncludeAttribute;
  547. if LIncludeAttribute.IncludeValue.Value = IncludeIf.CustomFunction then
  548. begin
  549. LMethodName := LIncludeAttribute.IncludeValue.IncludeFunction;
  550. LMethod := FParent.FType.GetMethod(LMethodName);
  551. if Assigned(LMethod) then
  552. begin
  553. LContext := TNeonIgnoreIfContext.Create(Self.Name, FOperation);
  554. LRes := LMethod.Invoke(TObject(FParent.Instance), [TValue.From<TNeonIgnoreIfContext>(LContext)]);
  555. case LRes.AsType<Boolean> of
  556. True: FNeonIncludeIf := TNeonIncludeOption.Include;
  557. False: FNeonIncludeIf := TNeonIncludeOption.Exclude;
  558. end;
  559. end;
  560. end;
  561. end;
  562. end;
  563. procedure TNeonRttiMember.SetValue(const AValue: TValue);
  564. begin
  565. case FMemberType of
  566. TNeonMemberType.Prop : MemberAsProperty.SetValue(FParent.Instance, AValue);
  567. TNeonMemberType.Field: MemberAsField.SetValue(FParent.Instance, AValue);
  568. end;
  569. end;
  570. function TNeonRttiMember.TypeKind: TTypeKind;
  571. begin
  572. Result := tkUnknown;
  573. case FMemberType of
  574. TNeonMemberType.Unknown: raise ENeonException.Create('Member type must be Field or Property');
  575. TNeonMemberType.Prop : Result := MemberAsProperty.PropertyType.TypeKind;
  576. TNeonMemberType.Field : Result := MemberAsField.FieldType.TypeKind;
  577. end;
  578. end;
  579. function TNeonRttiMember.Visibility: TMemberVisibility;
  580. begin
  581. Result := FMember.Visibility
  582. end;
  583. { TCaseAlgorithm }
  584. class function TCaseAlgorithm.CamelToPascal(const AString: string): string;
  585. var
  586. LOld, LNew: Char;
  587. begin
  588. Result := AString;
  589. if Result.IsEmpty then
  590. Exit;
  591. LOld := Result.Chars[0];
  592. LNew := UpperCase(LOld).Chars[0];
  593. Result := Result.Replace(LOld, LNew, []);
  594. end;
  595. class function TCaseAlgorithm.PascalToCamel(const AString: string): string;
  596. var
  597. LOld, LNew: Char;
  598. begin
  599. Result := AString;
  600. if Result.IsEmpty then
  601. Exit;
  602. LOld := Result.Chars[0];
  603. LNew := LowerCase(LOld).Chars[0];
  604. Result := Result.Replace(LOld, LNew, []);
  605. end;
  606. class function TCaseAlgorithm.PascalToSnake(const AString: string): string;
  607. begin
  608. Result := LowerCase(
  609. TRegEx.Replace(AString,
  610. '([A-Z][a-z\d]+)(?=([A-Z][A-Z\a-z\d]+))', '$1_', [])
  611. );
  612. end;
  613. class function TCaseAlgorithm.SnakeToPascal(const AString: string): string;
  614. var
  615. LChar: Char;
  616. LIndex: Integer;
  617. LSingleWord: string;
  618. LWords: TArray<string>;
  619. begin
  620. LWords := AString.Split(['_']);
  621. for LIndex := 0 to Length(LWords) - 1 do
  622. begin
  623. LSingleWord := LWords[LIndex];
  624. if LSingleWord.IsEmpty then
  625. Continue;
  626. LChar := Upcase(LSingleWord.Chars[0]);
  627. LSingleWord := LSingleWord.Remove(0, 1);
  628. LSingleWord := LSingleWord.Insert(0, LChar);
  629. LWords[LIndex] := LSingleWord;
  630. end;
  631. Result := string.Join('', LWords);
  632. end;
  633. { TNeonRttiMembers }
  634. constructor TNeonRttiMembers.Create(AConfig: TNeonConfiguration; AInstance: Pointer;
  635. AType: TRttiType; AOperation: TNeonOperation);
  636. begin
  637. inherited Create(True);
  638. FConfig := AConfig;
  639. FInstance := AInstance;
  640. FOperation := AOperation;
  641. FParent := TNeonRttiType.Create(AInstance, AType, AOperation);
  642. end;
  643. destructor TNeonRttiMembers.Destroy;
  644. begin
  645. FParent.Free;
  646. inherited;
  647. end;
  648. procedure TNeonRttiMembers.FilterDeserialize;
  649. var
  650. LMember: TNeonRttiMember;
  651. begin
  652. for LMember in Self do
  653. begin
  654. if LMember.NeonInclude.Present and (LMember.NeonInclude.Value = IncludeIf.Always) then
  655. begin
  656. LMember.Serializable := True;
  657. Continue;
  658. end;
  659. if LMember.NeonIgnore then
  660. Continue;
  661. if not LMember.IsWritable then
  662. Continue;
  663. if MatchesVisibility(LMember.Visibility) then
  664. if MatchesMemberChoice(LMember.MemberType) then
  665. LMember.Serializable := True;
  666. end;
  667. end;
  668. procedure TNeonRttiMembers.FilterSerialize;
  669. var
  670. LMember: TNeonRttiMember;
  671. begin
  672. for LMember in Self do
  673. begin
  674. if LMember.NeonInclude.Present and (LMember.NeonInclude.Value = IncludeIf.Always) then
  675. begin
  676. LMember.Serializable := True;
  677. Continue;
  678. end;
  679. if LMember.NeonIgnore then
  680. Continue;
  681. case LMember.NeonIncludeIf of
  682. TNeonIncludeOption.Include:
  683. begin
  684. LMember.Serializable := True;
  685. Continue;
  686. end;
  687. TNeonIncludeOption.Exclude:
  688. begin
  689. Continue;
  690. end;
  691. end;
  692. // Exclusions
  693. if not LMember.IsReadable then
  694. Continue;
  695. { TODO -opaolo -c : Maybe controlled by a config item? 29/06/2018 23:14:17 }
  696. if SameText(LMember.Name, 'Parent') then
  697. Continue;
  698. if SameText(LMember.Name, 'Owner') then
  699. Continue;
  700. if not LMember.IsWritable and
  701. not (LMember.TypeKind in [tkClass, tkInterface]) then
  702. Continue;
  703. if MatchesVisibility(LMember.Visibility) then
  704. if MatchesMemberChoice(LMember.MemberType) then
  705. LMember.Serializable := True;
  706. end;
  707. end;
  708. function TNeonRttiMembers.MatchesMemberChoice(AMemberType: TNeonMemberType): Boolean;
  709. var
  710. LRttiType: TRttiType;
  711. LMemberChoice: TNeonMembersSet;
  712. begin
  713. Result := False;
  714. if FParent.NeonMembers = [] then
  715. LMemberChoice := FConfig.Members
  716. else
  717. LMemberChoice := FParent.NeonMembers;
  718. if TNeonMembers.Standard in LMemberChoice then
  719. begin
  720. LRttiType := FParent.AsRttiType;
  721. if Assigned(LRttiType) then
  722. begin
  723. if LRttiType.IsRecord then
  724. LMemberChoice := LMemberChoice + [TNeonMembers.Fields];
  725. if LRttiType.IsInstance then
  726. LMemberChoice := LMemberChoice + [TNeonMembers.Properties];
  727. end;
  728. end;
  729. case AMemberType of
  730. //TNeonMemberType.Unknown: Result := False;
  731. TNeonMemberType.Prop : Result := TNeonMembers.Properties in LMemberChoice;
  732. TNeonMemberType.Field : Result := TNeonMembers.Fields in LMemberChoice;
  733. //TNeonMemberType.Indexed: Result := False;
  734. end;
  735. end;
  736. function TNeonRttiMembers.MatchesVisibility(AVisibility: TMemberVisibility): Boolean;
  737. var
  738. LVisibility: TNeonVisibility;
  739. begin
  740. Result := False;
  741. if FParent.NeonVisibility = [] then
  742. LVisibility := FConfig.Visibility
  743. else
  744. LVisibility := FParent.NeonVisibility;
  745. if AVisibility in LVisibility then
  746. Result := True;
  747. end;
  748. function TNeonRttiMembers.NewMember(AMember: TRttiMember): TNeonRttiMember;
  749. begin
  750. Result := TNeonRttiMember.Create(AMember, FParent, FOperation);
  751. end;
  752. { TNeonRttiObject }
  753. function TNeonRttiObject.AsRttiType: TRttiType;
  754. begin
  755. Result := nil;
  756. if FRttiObject is TRttiType then
  757. Result := FRttiObject as TRttiType;
  758. end;
  759. constructor TNeonRttiObject.Create(ARttiObject: TRttiObject; AOperation: TNeonOperation);
  760. begin
  761. FRttiObject := ARttiObject;
  762. FOperation := AOperation;
  763. FAttributes := FRttiObject.GetAttributes;
  764. FNeonMembers := [];
  765. end;
  766. procedure TNeonRttiObject.InternalParseAttributes(const AAttr: TArray<TCustomAttribute>);
  767. var
  768. LAttribute: TCustomAttribute;
  769. begin
  770. for LAttribute in AAttr do
  771. begin
  772. if LAttribute is NeonIncludeAttribute then
  773. FNeonInclude := (LAttribute as NeonIncludeAttribute).IncludeValue
  774. else if LAttribute is NeonSerializeAttribute then
  775. begin
  776. FNeonSerializerName := (LAttribute as NeonSerializeAttribute).Name;
  777. FNeonSerializerClass := (LAttribute as NeonSerializeAttribute).Clazz;
  778. end
  779. else if LAttribute is NeonIgnoreAttribute then
  780. FNeonIgnore := True
  781. else if LAttribute is NeonPropertyAttribute then
  782. FNeonProperty := (LAttribute as NeonPropertyAttribute).Value
  783. else if LAttribute is NeonEnumNamesAttribute then
  784. FNeonEnumNames := (LAttribute as NeonEnumNamesAttribute).Names
  785. else if LAttribute is NeonVisibilityAttribute then
  786. FNeonVisibility := (LAttribute as NeonVisibilityAttribute).Value
  787. else if LAttribute is NeonMembersSetAttribute then
  788. FNeonMembers := (LAttribute as NeonMembersSetAttribute).Value;
  789. // Further attribute processing
  790. ProcessAttribute(LAttribute);
  791. end;
  792. end;
  793. procedure TNeonRttiObject.ParseAttributes;
  794. begin
  795. if Length(FTypeAttributes) > 0 then
  796. InternalParseAttributes(FTypeAttributes);
  797. if Length(FAttributes) > 0 then
  798. InternalParseAttributes(FAttributes);
  799. end;
  800. procedure TNeonRttiObject.ProcessAttribute(AAttribute: TCustomAttribute);
  801. begin
  802. end;
  803. { TNeonRttiType }
  804. constructor TNeonRttiType.Create(AInstance: Pointer; AType: TRttiType; AOperation: TNeonOperation);
  805. begin
  806. inherited Create(AType, AOperation);
  807. FType := AType;
  808. FInstance := AInstance;
  809. ParseAttributes;
  810. end;
  811. { TNeonSerializerRegistry }
  812. procedure TNeonSerializerRegistry.Assign(ARegistry: TNeonSerializerRegistry);
  813. var
  814. LInfo: TSerializerInfo;
  815. LPair: TPair<PTypeInfo, TCustomSerializer>;
  816. begin
  817. for LInfo in ARegistry.FRegistryClass do
  818. FRegistryClass.Add(LInfo);
  819. for LPair in ARegistry.FRegistryCache do
  820. FRegistryCache.Add(LPair.Key, LPair.Value);
  821. end;
  822. procedure TNeonSerializerRegistry.Clear;
  823. begin
  824. FRegistryClass.Clear;
  825. FRegistryCache.Clear;
  826. end;
  827. procedure TNeonSerializerRegistry.ClearCache;
  828. begin
  829. FRegistryCache.Clear;
  830. end;
  831. constructor TNeonSerializerRegistry.Create;
  832. begin
  833. FRegistryClass := SerializerClassRegistry.Create();
  834. FRegistryCache := SerializerCacheRegistry.Create([doOwnsValues]);
  835. end;
  836. destructor TNeonSerializerRegistry.Destroy;
  837. begin
  838. FRegistryClass.Free;
  839. FRegistryCache.Free;
  840. inherited;
  841. end;
  842. function TNeonSerializerRegistry.GetCount: Integer;
  843. begin
  844. Result := FRegistryClass.Count;
  845. end;
  846. function TNeonSerializerRegistry.GetSerializer(AValue: TValue): TCustomSerializer;
  847. begin
  848. Result := InternalGetSerializer(AValue.TypeInfo);
  849. end;
  850. function TNeonSerializerRegistry.GetSerializer<T>: TCustomSerializer;
  851. begin
  852. Result := InternalGetSerializer(TypeInfo(T));
  853. end;
  854. function TNeonSerializerRegistry.GetSerializer(ATargetInfo: PTypeInfo): TCustomSerializer;
  855. begin
  856. Result := InternalGetSerializer(ATargetInfo);
  857. end;
  858. function TNeonSerializerRegistry.GetSerializer(ATargetClass: TClass): TCustomSerializer;
  859. begin
  860. Result := InternalGetSerializer(ATargetClass.ClassInfo);
  861. end;
  862. function TNeonSerializerRegistry.InternalGetSerializer(ATypeInfo: PTypeInfo): TCustomSerializer;
  863. var
  864. LInfo: TSerializerInfo;
  865. LClass: TCustomSerializerClass;
  866. LDistanceMax: Integer;
  867. begin
  868. Result := nil;
  869. LClass := nil;
  870. LDistanceMax := 0;
  871. if FRegistryCache.TryGetValue(ATypeInfo, Result) then
  872. Exit(Result);
  873. for LInfo in FRegistryClass do
  874. begin
  875. if LInfo.SerializerClass.CanHandle(ATypeInfo) then
  876. begin
  877. if LInfo.Distance = -1 then
  878. begin
  879. LClass := LInfo.SerializerClass;
  880. Break;
  881. end
  882. else
  883. begin
  884. if LInfo.Distance > LDistanceMax then
  885. begin
  886. LDistanceMax := LInfo.Distance;
  887. LClass := LInfo.SerializerClass;
  888. end;
  889. end;
  890. end;
  891. end;
  892. if Assigned(LClass) then
  893. begin
  894. Result := LClass.Create;
  895. FRegistryCache.Add(ATypeInfo, Result);
  896. end;
  897. end;
  898. function TNeonSerializerRegistry.RegisterSerializer(ASerializerClass: TCustomSerializerClass): TNeonSerializerRegistry;
  899. begin
  900. FRegistryClass.Add(TSerializerInfo.FromSerializer(ASerializerClass));
  901. Result := Self;
  902. end;
  903. procedure TNeonSerializerRegistry.UnregisterSerializer(ASerializerClass: TCustomSerializerClass);
  904. var
  905. LIndex: Integer;
  906. begin
  907. for LIndex := 0 to FRegistryClass.Count - 1 do
  908. if FRegistryClass[LIndex].SerializerClass = ASerializerClass then
  909. begin
  910. FRegistryClass.Delete(LIndex);
  911. ClearCache;
  912. Break;
  913. end;
  914. end;
  915. { TCustomSerializer }
  916. class function TCustomSerializer.ClassDistance: Integer;
  917. begin
  918. Result := TRttiUtils.ClassDistanceFromRoot(GetTargetInfo);
  919. end;
  920. class function TCustomSerializer.ClassIs(AClass: TClass): Boolean;
  921. var
  922. LType: TRttiType;
  923. begin
  924. Result := False;
  925. LType := TRttiUtils.Context.GetType(GetTargetInfo);
  926. if Assigned(LType) and (LType.TypeKind = tkClass) then
  927. Result := AClass.InheritsFrom(LType.AsInstance.MetaclassType);
  928. end;
  929. class function TCustomSerializer.GetTargetInfo: PTypeInfo;
  930. begin
  931. Result := nil;
  932. end;
  933. class function TCustomSerializer.TypeInfoIs(AInfo: PTypeInfo): Boolean;
  934. var
  935. LType: TRttiType;
  936. begin
  937. Result := False;
  938. LType := TRttiUtils.Context.GetType(AInfo);
  939. if Assigned(LType) and (LType.TypeKind = tkClass) then
  940. Result := ClassIs(LType.AsInstance.MetaclassType);
  941. end;
  942. class function TCustomSerializer.TypeInfoIsClass(AInfo: PTypeInfo): Boolean;
  943. var
  944. LType: TRttiType;
  945. begin
  946. Result := False;
  947. LType := TRttiUtils.Context.GetType(AInfo);
  948. if Assigned(LType) and (LType.TypeKind = tkClass) then
  949. Result := True;
  950. end;
  951. { TSerializerInfo }
  952. class function TSerializerInfo.FromSerializer(ASerializerClass: TCustomSerializerClass): TSerializerInfo;
  953. begin
  954. Result.SerializerClass := ASerializerClass;
  955. Result.Distance := ASerializerClass.ClassDistance;
  956. end;
  957. end.