fpjson.schema.codegen.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. {
  2. This file is part of the Free Component Library
  3. Copyright (c) 2024 by Michael Van Canneyt [email protected]
  4. JSON Schema - pascal code generator
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit fpjson.schema.codegen;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. {$IFDEF FPC_DOTTEDUNITS}
  16. System.Classes, System.SysUtils, System.DateUtils, Pascal.CodeGenerator,
  17. {$ELSE}
  18. Classes, SysUtils, dateutils, pascodegen,
  19. {$ENDIF}
  20. fpjson.schema.types,
  21. fpjson.schema.Pascaltypes;
  22. Type
  23. { TJSONSchemaCodeGen }
  24. { TJSONSchemaCodeGenerator }
  25. TJSONSchemaCodeGenerator = class(TPascalCodeGenerator)
  26. private
  27. FData: TSchemaData;
  28. FDelphiCode: boolean;
  29. FVerboseHeader: Boolean;
  30. FWriteClassType: boolean;
  31. protected
  32. procedure GenerateHeader; virtual;
  33. procedure GenerateFPCDirectives(modeswitches : array of string);
  34. procedure GenerateFPCDirectives();
  35. function GetPascalTypeAndDefault(aType: TSchemaSimpleType; out aPasType, aPasDefault: string) : boolean;
  36. function GetJSONDefault(aType: TPropertyType) : String;
  37. procedure SetTypeData(aData : TSchemaData);
  38. public
  39. Property TypeData : TSchemaData Read FData;
  40. property DelphiCode: boolean read FDelphiCode write FDelphiCode;
  41. Property VerboseHeader : Boolean Read FVerboseHeader Write FVerboseHeader;
  42. property WriteClassType: boolean read FWriteClassType write FWriteClassType;
  43. end;
  44. { TTypeCodeGenerator }
  45. TTypeCodeGenerator = class(TJSONSchemaCodeGenerator)
  46. private
  47. FTypeParentClass: string;
  48. procedure WriteDtoConstructor(aType: TPascalTypeData);
  49. procedure WriteDtoField(aType: TPascalTypeData; aProperty: TPascalPropertyData);
  50. procedure WriteDtoType(aType: TPascalTypeData);
  51. procedure WriteDtoArrayType(aType: TPascalTypeData);
  52. public
  53. constructor Create(AOwner: TComponent); override;
  54. procedure Execute(aData: TSchemaData);
  55. property TypeParentClass: string read FTypeParentClass write FTypeParentClass;
  56. end;
  57. { TSerializerCodeGen }
  58. { TSerializerCodeGenerator }
  59. TSerializerCodeGenerator = class(TJSONSchemaCodeGenerator)
  60. const
  61. Bools : Array[Boolean] of String = ('False','True');
  62. private
  63. FConvertUTC: Boolean;
  64. FDataUnitName: string;
  65. function FieldToJSON(aProperty: TPascalPropertyData) : string;
  66. function ArrayMemberToField(aType: TPropertyType; const aPropertyTypeName: String; const aFieldName: string): string;
  67. function FieldToJSON(aType: TPropertyType; aFieldName: String): string;
  68. procedure GenerateConverters;
  69. function JSONToField(aProperty: TPascalPropertyData) : string;
  70. function JSONToField(aType: TPropertyType; const aPropertyTypeName: string; const aKeyName: string): string;
  71. procedure WriteFieldDeSerializer(aType : TPascalTypeData; aProperty: TPascalPropertyData);
  72. procedure WriteFieldSerializer(aType : TPascalTypeData; aProperty: TPascalPropertyData);
  73. procedure WriteDtoObjectSerializer(aType: TPascalTypeData);
  74. procedure WriteDtoSerializer(aType: TPascalTypeData);
  75. procedure WriteDtoObjectDeserializer(aType: TPascalTypeData);
  76. procedure WriteDtoDeserializer(aType: TPascalTypeData);
  77. procedure WriteDtoHelper(aType: TPascalTypeData);
  78. public
  79. procedure Execute(aData: TSchemaData);
  80. property DataUnitName: string read FDataUnitName write FDataUnitName;
  81. property ConvertUTC : Boolean Read FConvertUTC Write FConvertUTC;
  82. end;
  83. implementation
  84. function TJSONSchemaCodeGenerator.GetPascalTypeAndDefault(
  85. aType: TSchemaSimpleType; out aPasType, aPasDefault: string) : boolean;
  86. begin
  87. Result := True;
  88. case aType of
  89. sstInteger:
  90. begin
  91. aPasType := FData.TypeMap['integer'];
  92. aPasDefault := '0';
  93. end;
  94. sstNumber:
  95. begin
  96. aPasType := FData.TypeMap['number'];
  97. aPasDefault := '0';
  98. end;
  99. sstBoolean:
  100. begin
  101. aPasType := FData.TypeMap['boolean'];
  102. aPasDefault := 'False';
  103. end;
  104. sstString:
  105. begin
  106. aPasType := FData.TypeMap['string'];
  107. aPasDefault := '''''';
  108. end;
  109. sstObject:
  110. begin
  111. aPasType := 'TJSONObject';
  112. aPasDefault := 'TJSONObject(Nil)';
  113. end;
  114. sstArray:
  115. begin
  116. aPasType := 'TJSONArray';
  117. aPasDefault := 'TJSONArray(Nil)';
  118. end;
  119. else
  120. Result := False;
  121. end;
  122. end;
  123. function TJSONSchemaCodeGenerator.GetJSONDefault(aType: TPropertyType): String;
  124. begin
  125. case aType of
  126. ptEnum:
  127. Result:='''''';
  128. ptDateTime:
  129. Result:='''''';
  130. ptInteger,
  131. ptInt64:
  132. Result:='0';
  133. ptfloat32,
  134. ptfloat64:
  135. Result := '0.0';
  136. ptBoolean:
  137. Result := 'False';
  138. ptJSON,
  139. ptString:
  140. Result := '''''';
  141. ptAnonStruct:
  142. Result := 'TJSONObject(Nil)';
  143. ptArray:
  144. Result := 'TJSONArray(Nil)';
  145. end;
  146. end;
  147. procedure TJSONSchemaCodeGenerator.SetTypeData(aData: TSchemaData);
  148. begin
  149. FData:=aData;
  150. end;
  151. procedure TJSONSchemaCodeGenerator.GenerateHeader;
  152. begin
  153. // Do nothing
  154. end;
  155. procedure TJSONSchemaCodeGenerator.GenerateFPCDirectives(modeswitches: array of string);
  156. var
  157. S : String;
  158. begin
  159. if DelphiCode then
  160. begin
  161. Addln('{$ifdef FPC}');
  162. AddLn('{$mode delphi}');
  163. end
  164. else
  165. AddLn('{$mode objfpc}');
  166. AddLn('{$h+}');
  167. for S in modeswitches do
  168. AddLn('{$modeswitch %s}',[lowercase(S)]);
  169. if DelphiCode then
  170. Addln('{$endif FPC}');
  171. Addln('');
  172. end;
  173. procedure TJSONSchemaCodeGenerator.GenerateFPCDirectives;
  174. begin
  175. GenerateFPCDirectives([]);
  176. end;
  177. { TTypeCodeGenerator }
  178. procedure TTypeCodeGenerator.WriteDtoField(aType: TPascalTypeData; aProperty: TPascalPropertyData);
  179. var
  180. lFieldName, lTypeName: string;
  181. begin
  182. lFieldName := aProperty.PascalName;
  183. lTypeName := aProperty.PascalTypeName;
  184. if lTypeName = '' then
  185. Addln('// Unknown type for field %s...', [lFieldName])
  186. else
  187. Addln('%s : %s;', [lFieldName, lTypeName]);
  188. end;
  189. procedure TTypeCodeGenerator.WriteDtoConstructor(aType: TPascalTypeData);
  190. var
  191. I : Integer;
  192. lProp : TPascalPropertyData;
  193. lConstructor : String;
  194. begin
  195. Addln('constructor %s.CreateWithMembers;',[aType.PascalName]);
  196. Addln('');
  197. Addln('begin');
  198. indent;
  199. For I:=0 to aType.PropertyCount-1 do
  200. begin
  201. lProp:=aType.Properties[i];
  202. if lProp.PropertyType=ptSchemaStruct then
  203. begin
  204. if lProp.TypeData.HasObjectProperty(True) then
  205. lConstructor:='CreateWithMembers'
  206. else
  207. lConstructor:='Create';
  208. AddLn('%s := %s.%s;',[lProp.PascalName,lProp.TypeData.PascalName,lConstructor]);
  209. end;
  210. end;
  211. Undent;
  212. Addln('end;');
  213. Addln('');
  214. end;
  215. procedure TTypeCodeGenerator.WriteDtoType(aType: TPascalTypeData);
  216. var
  217. I: integer;
  218. begin
  219. if WriteClassType then
  220. Addln('%s = Class(%s)', [aType.PascalName, TypeParentClass])
  221. else
  222. Addln('%s = record', [aType.PascalName]);
  223. indent;
  224. for I:=0 to aType.PropertyCount-1 do
  225. WriteDtoField(aType,aType.Properties[i]);
  226. if WriteClassType and aType.HasObjectProperty(True) then
  227. Addln('constructor CreateWithMembers;');
  228. undent;
  229. Addln('end;');
  230. Addln('');
  231. end;
  232. procedure TTypeCodeGenerator.WriteDtoArrayType(aType: TPascalTypeData);
  233. var
  234. Fmt : String;
  235. begin
  236. if DelphiCode then
  237. Fmt:='%s = TArray<%s>;'
  238. else
  239. Fmt:='%s = Array of %s;';
  240. Addln(Fmt,[aType.PascalName,aType.ElementTypeData.PascalName]);
  241. end;
  242. constructor TTypeCodeGenerator.Create(AOwner: TComponent);
  243. begin
  244. inherited Create(AOwner);
  245. TypeParentClass := 'TObject';
  246. end;
  247. procedure TTypeCodeGenerator.Execute(aData: TSchemaData);
  248. var
  249. I: integer;
  250. lArray : TPascalTypeData;
  251. begin
  252. FData := aData;
  253. GenerateHeader;
  254. try
  255. Addln('unit %s;', [OutputUnitName]);
  256. Addln('');
  257. GenerateFPCDirectives();
  258. Addln('');
  259. Addln('interface');
  260. Addln('');
  261. if DelphiCode then
  262. AddLn('uses System.Types;')
  263. else
  264. AddLn('uses types;');
  265. Addln('');
  266. EnsureSection(csType);
  267. Addln('');
  268. indent;
  269. for I := 0 to aData.TypeCount-1 do
  270. if aData.Types[I].PascalType in [ptSchemaStruct,ptAnonStruct] then
  271. begin
  272. DoLog('Generating type %s', [aData.Types[I].PascalName]);
  273. WriteDtoType(aData.Types[I]);
  274. lArray:=aData.FindSchemaTypeData('['+aData.Types[I].SchemaName+']');
  275. if lArray<>Nil then
  276. WriteDtoArrayType(lArray);
  277. end;
  278. { else if (aData.Types[I].PascalType=ptArray) then
  279. WriteDtoArrayType(aData.Types[I]);}
  280. undent;
  281. Addln('implementation');
  282. Addln('');
  283. if WriteClassType then
  284. for I := 0 to aData.TypeCount-1 do
  285. begin
  286. if (aData.Types[I].PascalType in [ptSchemaStruct,ptAnonStruct])
  287. and aData.Types[I].HasObjectProperty(True) then
  288. begin
  289. DoLog('Generating type %s constructor', [aData.Types[I].PascalName]);
  290. WriteDtoConstructor(aData.Types[I]);
  291. end;
  292. end;
  293. Addln('end.');
  294. finally
  295. FData := nil;
  296. end;
  297. end;
  298. { TSerializerCodeGenerator }
  299. function TSerializerCodeGenerator.FieldToJSON(aProperty: TPascalPropertyData): string;
  300. begin
  301. Result:=FieldToJSON(aProperty.PropertyType,aProperty.PascalName)
  302. end;
  303. function TSerializerCodeGenerator.FieldToJSON(aType: TPropertyType; aFieldName : String): string;
  304. begin
  305. if aType in [ptAnonStruct,ptSchemaStruct] then
  306. begin
  307. Result := Format('%s.SerializeObject', [aFieldName]);
  308. end
  309. else
  310. begin
  311. case aType of
  312. ptBoolean:
  313. if DelphiCode then
  314. Result := Format('TJSONBool.Create(%s)', [aFieldName])
  315. else
  316. Result := aFieldName;
  317. ptJSON:
  318. if DelphiCode then
  319. Result := Format('TJSONObject.ParseJSONValue(%s,True,True)', [aFieldName])
  320. else
  321. Result := Format('GetJSON(%s)', [aFieldName]);
  322. ptDateTime :
  323. Result := Format('DateToISO8601(%s,%s)', [aFieldName,Bools[Not ConvertUTC]]);
  324. ptEnum :
  325. Result := Format('%s.AsString', [aFieldName]);
  326. else
  327. Result := aFieldName;
  328. end;
  329. end;
  330. end;
  331. function TSerializerCodeGenerator.JSONToField(aProperty : TPascalPropertyData): string;
  332. begin
  333. Result:=JSONToField(aProperty.PropertyType,aProperty.TypeNames[ntPascal], aProperty.SchemaName);
  334. end;
  335. function TSerializerCodeGenerator.JSONToField(aType: TPropertyType; const aPropertyTypeName: string; const aKeyName: string): string;
  336. function ObjectField(lName: string) : string;
  337. begin
  338. if DelphiCode then
  339. Result := Format('aJSON.GetValue<TJSONObject>(''%s'',Nil)', [lName])
  340. else
  341. Result := Format('aJSON.Get(''%s'',TJSONObject(Nil))', [lName]);
  342. end;
  343. var
  344. lPropType,
  345. lPasDefault: string;
  346. begin
  347. if aType in [ptSchemaStruct,ptAnonStruct] then
  348. begin
  349. Result := Format('%s.Deserialize(%s)', [aPropertyTypeName, ObjectField(aKeyName)]);
  350. end
  351. else
  352. begin
  353. case aType of
  354. ptString,
  355. ptFloat32,
  356. ptFloat64,
  357. ptDateTime,
  358. ptEnum,
  359. ptInteger,
  360. ptInt64,
  361. ptBoolean:
  362. begin
  363. if aType=ptDateTime then
  364. lPropType:='string'
  365. else
  366. lPropType:=aPropertyTypeName;
  367. lPasDefault:=GetJSONDefault(aType);
  368. if DelphiCode then
  369. Result := Format('aJSON.GetValue<%s>(''%s'',%s)', [lPropType, aKeyName, lPasDefault])
  370. else
  371. Result := Format('aJSON.Get(''%s'',%s)', [aKeyName, lPasDefault]);
  372. end;
  373. ptJSON:
  374. begin
  375. if DelphiCode then
  376. Result := ObjectField(aKeyName)+'.ToJSON'
  377. else
  378. Result := ObjectField(aKeyName)+'.AsJSON';
  379. end;
  380. else
  381. Result := aKeyName;
  382. end;
  383. end;
  384. end;
  385. function TSerializerCodeGenerator.ArrayMemberToField(aType: TPropertyType; const aPropertyTypeName : String; const aFieldName: string): string;
  386. var
  387. lPasDefault: string;
  388. begin
  389. if aType in [ptAnonStruct,ptSchemaStruct] then
  390. Result := Format('%s.Deserialize(%s as TJSONObject)', [aPropertyTypeName, aFieldName])
  391. else
  392. begin
  393. case aType of
  394. ptEnum:
  395. begin
  396. lPasDefault:=GetJSONDefault(aType);
  397. if DelphiCode then
  398. Result := Format('%s.GetValue<String>('''',%s)', [aFieldName, lPasDefault])
  399. else
  400. Result := Format('%s.AsString', [aFieldName]);
  401. end;
  402. ptDateTime:
  403. Result := Format('%s.AsString', [aFieldName]);
  404. ptString,
  405. ptFloat32,
  406. ptFloat64,
  407. ptInteger,
  408. ptInt64,
  409. ptBoolean:
  410. begin
  411. lPasDefault:=GetJSONDefault(aType);
  412. if DelphiCode then
  413. Result := Format('%s.GetValue<%s>('''',%s)', [aFieldName, aPropertyTypeName, lPasDefault])
  414. else
  415. Result := Format('%s.As%s', [aFieldName, aPropertyTypeName]);
  416. end;
  417. ptAnonStruct:
  418. begin
  419. if DelphiCode then
  420. Result := Format('%s.ToJSON', [aFieldName])
  421. else
  422. Result := Format('%s.AsJSON', [aFieldName]);
  423. end;
  424. else
  425. Result := aFieldName;
  426. end;
  427. end;
  428. end;
  429. procedure TSerializerCodeGenerator.WriteFieldSerializer(aType : TPascalTypeData; aProperty: TPascalPropertyData);
  430. var
  431. lAssign, lValue, lKeyName, lFieldName: string;
  432. lType: TPropertyType;
  433. lNilCheck : Boolean;
  434. begin
  435. lKeyName := aProperty.SchemaName;
  436. lFieldName := aProperty.PascalName;
  437. lValue := FieldToJSON(aProperty);
  438. lType:=aProperty.PropertyType;
  439. lNilCheck:=WriteClassType and (lType in [ptJSON,ptAnonStruct,ptSchemaStruct]);
  440. case lType of
  441. ptEnum:
  442. begin
  443. Addln('if (%s<>%s._empty_) then',[lFieldName,aProperty.PascalTypeName]);
  444. indent;
  445. if DelphiCode then
  446. Addln('Result.AddPair(''%s'',%s);', [lKeyName, lValue])
  447. else
  448. Addln('Result.Add(''%s'',%s);', [lKeyName, lValue]);
  449. undent;
  450. end;
  451. ptDatetime,
  452. ptInteger,
  453. ptInt64,
  454. ptString,
  455. ptBoolean,
  456. ptFloat32,
  457. ptFloat64,
  458. ptJSON,
  459. ptSchemaStruct:
  460. begin
  461. if lNilCheck then
  462. begin
  463. if (lType=ptJSON) then
  464. // JSON string...
  465. AddLn('if (%s<>'''') then',[lFieldName])
  466. else
  467. AddLn('if Assigned(%s) then',[lFieldName]);
  468. indent;
  469. end;
  470. if DelphiCode then
  471. Addln('Result.AddPair(''%s'',%s);', [lKeyName, lValue])
  472. else
  473. Addln('Result.Add(''%s'',%s);', [lKeyName, lValue]);
  474. if lNilCheck then
  475. undent;
  476. end;
  477. ptArray:
  478. begin
  479. Addln('Arr:=TJSONArray.Create;');
  480. if DelphiCode then
  481. Addln('Result.AddPair(''%s'',Arr);', [lKeyName])
  482. else
  483. Addln('Result.Add(''%s'',Arr);', [lKeyName]);
  484. lAssign := Format('%s[i]', [lFieldName]);
  485. lAssign := FieldToJSON(aProperty.ElementType, lAssign);
  486. Addln('For I:=0 to Length(%s)-1 do', [lFieldName]);
  487. indent;
  488. Addln('Arr.Add(%s);', [lAssign]);
  489. undent;
  490. end;
  491. else
  492. DoLog('Unknown type for property %s', [aProperty.PascalName]);
  493. end;
  494. end;
  495. procedure TSerializerCodeGenerator.WriteFieldDeSerializer(aType: TPascalTypeData; aProperty: TPascalPropertyData);
  496. var
  497. lElName, lValue, lKeyName, lFieldName: string;
  498. begin
  499. lKeyName := aProperty.SchemaName;
  500. lFieldName := aProperty.PascalName;
  501. if aProperty.PropertyType<>ptArray then
  502. lValue := JSONToField(aProperty)
  503. else
  504. lValue := ArrayMemberToField(aProperty.ElementType,aProperty.ElementTypeName,'lArr[i]');
  505. case aProperty.PropertyType of
  506. ptEnum :
  507. Addln('Result.%s.AsString:=%s;', [lFieldName, lValue]);
  508. ptDateTime:
  509. begin
  510. Addln('Result.%s:=ISO8601ToDateDef(%s,0,%s);', [lFieldName, lValue, Bools[Not ConvertUTC]]);
  511. end;
  512. ptInteger,
  513. ptInt64,
  514. ptFloat32,
  515. ptFloat64,
  516. ptString,
  517. ptBoolean,
  518. ptAnonStruct,
  519. ptJSON,
  520. ptSchemaStruct:
  521. Addln('Result.%s:=%s;', [lFieldName, lValue]);
  522. ptArray:
  523. begin
  524. if DelphiCode then
  525. Addln('lArr:=aJSON.GetValue<TJSONArray>(''%s'',Nil);', [lKeyName])
  526. else
  527. Addln('lArr:=aJSON.Get(''%s'',TJSONArray(Nil));', [lKeyName]);
  528. Addln('if Assigned(lArr) then');
  529. indent;
  530. Addln('begin');
  531. Addln('SetLength(Result.%s,lArr.Count);', [lFieldName]);
  532. lElName := Format('%s[i]', [lFieldName]);
  533. Addln('For I:=0 to Length(Result.%s)-1 do', [lFieldName]);
  534. indent;
  535. Addln('Result.%s:=%s;', [lElName, lValue]);
  536. undent;
  537. Addln('end;');
  538. undent;
  539. end;
  540. else
  541. DoLog('Unknown type for property %s', [aProperty.PascalName]);
  542. end;
  543. end;
  544. procedure TSerializerCodeGenerator.WriteDtoObjectSerializer(aType: TPascalTypeData);
  545. var
  546. I: integer;
  547. lName: string;
  548. begin
  549. lName := aType.SerializerName;
  550. Addln('function %s.SerializeObject : TJSONObject;', [lName]);
  551. Addln('');
  552. if aType.HasArrayProperty then
  553. begin
  554. Addln('var');
  555. indent;
  556. Addln('i : integer;');
  557. Addln('Arr : TJSONArray;');
  558. undent;
  559. Addln('');
  560. end;
  561. Addln('begin');
  562. indent;
  563. Addln('Result:=TJSONObject.Create;');
  564. Addln('try');
  565. indent;
  566. for I := 0 to aType.PropertyCount-1 do
  567. WriteFieldSerializer(aType, aType.Properties[I]);
  568. undent;
  569. Addln('except');
  570. indent;
  571. Addln('Result.Free;');
  572. Addln('raise;');
  573. undent;
  574. Addln('end;');
  575. undent;
  576. Addln('end;');
  577. Addln('');
  578. end;
  579. procedure TSerializerCodeGenerator.WriteDtoSerializer(aType: TPascalTypeData);
  580. var
  581. lName: string;
  582. begin
  583. lName := aType.SerializerName;
  584. Addln('function %s.Serialize : String;', [lName]);
  585. Addln('var');
  586. indent;
  587. Addln('lObj : TJSONObject;');
  588. undent;
  589. Addln('begin');
  590. indent;
  591. Addln('lObj:=SerializeObject;');
  592. Addln('try');
  593. indent;
  594. if DelphiCode then
  595. Addln('Result:=lObj.ToJSON;')
  596. else
  597. Addln('Result:=lObj.AsJSON;');
  598. undent;
  599. Addln('finally');
  600. indent;
  601. Addln('lObj.Free');
  602. undent;
  603. Addln('end;');
  604. undent;
  605. Addln('end;');
  606. Addln('');
  607. end;
  608. procedure TSerializerCodeGenerator.WriteDtoObjectDeserializer(aType: TPascalTypeData);
  609. var
  610. I: integer;
  611. lHasArray: boolean;
  612. begin
  613. Addln('class function %s.Deserialize(aJSON : TJSONObject) : %s;', [aType.SerializerName, aType.PascalName]);
  614. Addln('');
  615. lHasArray := aType.HasArrayProperty;
  616. // lHasObject:=aType.HasObjectProperty(True);
  617. if lHasArray then
  618. begin
  619. Addln('var');
  620. indent;
  621. if lHasArray then
  622. begin
  623. Addln('lArr : TJSONArray;');
  624. Addln('i : Integer;');
  625. end;
  626. undent;
  627. end;
  628. undent;
  629. Addln('begin');
  630. indent;
  631. if WriteClassType then
  632. Addln('Result := %s.Create;', [aType.PascalName])
  633. else
  634. Addln('Result := Default(%s);', [aType.PascalName]);
  635. Addln('If (aJSON=Nil) then');
  636. indent;
  637. Addln('exit;');
  638. undent;
  639. for I := 0 to aType.PropertyCount-1 do
  640. WriteFieldDeSerializer(aType, aType.Properties[I]);
  641. undent;
  642. Addln('end;');
  643. Addln('');
  644. end;
  645. procedure TSerializerCodeGenerator.WriteDtoDeserializer(aType: TPascalTypeData);
  646. begin
  647. Addln('class function %s.Deserialize(aJSON : String) : %s;', [aType.SerializerName, aType.PascalName]);
  648. Addln('');
  649. Addln('var');
  650. indent;
  651. Addln('lObj : TJSONObject;');
  652. undent;
  653. Addln('begin');
  654. indent;
  655. Addln('Result := Default(%s);', [aType.PascalName]);
  656. Addln('if (aJSON='''') then');
  657. indent;
  658. Addln('exit;');
  659. undent;
  660. if DelphiCode then
  661. Addln('lObj := TJSONObject.ParseJSONValue(aJSON,True,True) as TJSONObject;')
  662. else
  663. Addln('lObj := GetJSON(aJSON) as TJSONObject;');
  664. Addln('if (lObj = nil) then');
  665. indent;
  666. Addln('exit;');
  667. undent;
  668. Addln('try');
  669. indent;
  670. Addln('Result:=Deserialize(lObj);');
  671. undent;
  672. Addln('finally');
  673. indent;
  674. Addln('lObj.Free');
  675. undent;
  676. Addln('end;');
  677. undent;
  678. Addln('end;');
  679. Addln('');
  680. end;
  681. procedure TSerializerCodeGenerator.WriteDtoHelper(aType: TPascalTypeData);
  682. begin
  683. if WriteClassType then
  684. Addln('%s = class helper for %s', [aType.SerializerName, aType.PascalName])
  685. else
  686. if DelphiCode then
  687. Addln('%s = record helper for %s', [aType.SerializerName, aType.PascalName])
  688. else
  689. Addln('%s = type helper for %s', [aType.SerializerName, aType.PascalName]);
  690. indent;
  691. if stSerialize in aType.SerializeTypes then
  692. begin
  693. Addln('function SerializeObject : TJSONObject;');
  694. Addln('function Serialize : String;');
  695. end;
  696. if stDeserialize in aType.SerializeTypes then
  697. begin
  698. Addln('class function Deserialize(aJSON : TJSONObject) : %s; overload; static;', [aType.PascalName]);
  699. Addln('class function Deserialize(aJSON : String) : %s; overload; static;', [aType.PascalName]);
  700. end;
  701. undent;
  702. Addln('end;');
  703. end;
  704. procedure TSerializerCodeGenerator.GenerateConverters;
  705. begin
  706. Addln('function ISO8601ToDateDef(S: String; aDefault : TDateTime; aConvertUTC: Boolean = True) : TDateTime;');
  707. Addln('');
  708. Addln('begin');
  709. indent;
  710. Addln('if (S='''') then');
  711. indent;
  712. Addln('Exit(aDefault);');
  713. undent;
  714. Addln('try');
  715. indent;
  716. AddLn('Result:=ISO8601ToDate(S,aConvertUTC);');
  717. undent;
  718. Addln('except');
  719. indent;
  720. Addln('Result:=aDefault;');
  721. undent;
  722. Addln('end;');
  723. undent;
  724. Addln('end;');
  725. Addln('');
  726. end;
  727. procedure TSerializerCodeGenerator.Execute(aData: TSchemaData);
  728. var
  729. I: integer;
  730. lType: TPascalTypeData;
  731. begin
  732. FData := aData;
  733. GenerateHeader;
  734. try
  735. Addln('unit %s;', [OutputUnitName]);
  736. Addln('');
  737. Addln('interface');
  738. Addln('');
  739. GenerateFPCDirectives(['typehelpers']);
  740. Addln('');
  741. Addln('uses');
  742. indent;
  743. if DelphiCode then
  744. Addln('System.JSON,')
  745. else
  746. Addln('fpJSON,');
  747. Addln(DataUnitName+';');
  748. undent;
  749. Addln('');
  750. EnsureSection(csType);
  751. indent;
  752. for I := 0 to aData.TypeCount-1 do
  753. begin
  754. with aData.Types[I] do
  755. if Pascaltype in [ptSchemaStruct,ptAnonStruct] then
  756. begin
  757. DoLog('Generating serialization helper type %s for Dto %s', [SerializerName, PascalName]);
  758. WriteDtoHelper(aData.Types[I]);
  759. Addln('');
  760. end;
  761. end;
  762. undent;
  763. Addln('implementation');
  764. Addln('');
  765. if DelphiCode then
  766. Addln('uses System.Generics.Collections, System.SysUtils, System.Types, System.DateUtils, System.StrUtils;')
  767. else
  768. Addln('uses Generics.Collections, SysUtils, Types, DateUtils, StrUtils;');
  769. Addln('');
  770. GenerateConverters;
  771. for I := 0 to aData.TypeCount-1 do
  772. begin
  773. lType := aData.Types[I];
  774. if LType.Pascaltype in [ptSchemaStruct,ptAnonStruct] then
  775. begin
  776. if stSerialize in lType.SerializeTypes then
  777. begin
  778. WriteDtoObjectSerializer(aData.Types[I]);
  779. WriteDtoSerializer(aData.Types[I]);
  780. end;
  781. if stDeserialize in lType.SerializeTypes then
  782. begin
  783. WriteDtoObjectDeserializer(aData.Types[I]);
  784. WriteDtoDeserializer(aData.Types[I]);
  785. end;
  786. end;
  787. end;
  788. Addln('');
  789. Addln('end.');
  790. finally
  791. FData := nil;
  792. end;
  793. end;
  794. end.