2
0

Quick.Json.Serializer.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. { ***************************************************************************
  2. Copyright (c) 2015-2018 Kike Pérez
  3. Unit : Quick.JSON.Serializer
  4. Description : Json Serializer
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 21/05/2018
  8. Modified : 08/06/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  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. unit Quick.Json.Serializer;
  22. interface
  23. uses
  24. System.Classes,
  25. System.SysUtils,
  26. System.Rtti,
  27. System.TypInfo,
  28. System.Json,
  29. System.DateUtils,
  30. Quick.Commons;
  31. type
  32. EJsonSerializeError = class(Exception)
  33. end;
  34. TNotSerializableProperty = class(TCustomAttribute);
  35. TCommentProperty = class(TCustomAttribute)
  36. private
  37. fComment : string;
  38. public
  39. constructor Create(const aComment: string);
  40. property Comment : string read fComment;
  41. end;
  42. TCustomNameProperty = class(TCustomAttribute)
  43. private
  44. fName : string;
  45. public
  46. constructor Create(const aName: string);
  47. property Name : string read fName;
  48. end;
  49. IJsonSerializer = interface
  50. ['{CA26F7AE-F1FE-41BE-9C23-723A687F60D1}']
  51. function JsonToObject(aType: TClass; const aJson: string): TObject; overload;
  52. function JsonToObject(aObject: TObject; const aJson: string): TObject; overload;
  53. function ObjectToJson(aObject: TObject): string;
  54. end;
  55. TJsonSerializer = class(TInterfacedObject,IJsonSerializer)
  56. strict private
  57. function GetValue(aAddr: Pointer; aType: TRTTIType): TValue;
  58. procedure DeserializeDynArray(aProperty : TRttiProperty; aObject : TObject; const aJsonArray: TJSONArray);
  59. function DeserializeRecord(aRecord : TValue; aObject : TObject; const aJson : TJSONObject) : TValue;
  60. function DeserializeClass(aType : TClass; const aJson : TJSONObject) : TObject;
  61. function DeserializeObject(aObject : TObject; const aJson : TJSONObject) : TObject; overload;
  62. function DeserializeObject(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aJson : TJSONObject) : TObject; overload;
  63. function DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
  64. function Serialize(const aName : string; aValue : TValue) : TJSONPair; overload;
  65. function Serialize(aObject : TObject) : TJSONObject; overload;
  66. public
  67. function JsonToObject(aType : TClass; const aJson: string) : TObject; overload;
  68. function JsonToObject(aObject : TObject; const aJson: string) : TObject; overload;
  69. function ObjectToJson(aObject : TObject): string;
  70. end;
  71. PPByte = ^PByte;
  72. resourcestring
  73. cNotSupportedDataType = 'Not supported "%s" data type "%s"';
  74. cNotSerializable = 'Object is not serializable';
  75. implementation
  76. { TqlJsonSerializer }
  77. procedure TJsonSerializer.DeserializeDynArray(aProperty: TRttiProperty; aObject: TObject; const aJsonArray: TJSONArray);
  78. var
  79. rType: PTypeInfo;
  80. len: NativeInt;
  81. pArr: Pointer;
  82. rValue : TValue;
  83. rItemValue: TValue;
  84. i: Integer;
  85. objClass: TClass;
  86. ctx : TRttiContext;
  87. rRec : TRttiRecordType;
  88. json : TJSONObject;
  89. rDynArray : TRttiDynamicArrayType;
  90. begin
  91. if GetTypeData(aProperty.PropertyType.Handle).DynArrElType = nil then Exit;
  92. len := aJsonArray.Count;
  93. rType := GetTypeData(aProperty.PropertyType.Handle).DynArrElType^;
  94. pArr := nil;
  95. DynArraySetLength(pArr, aProperty.PropertyType.Handle, 1, @len);
  96. try
  97. TValue.Make(@pArr, aProperty.PropertyType.Handle, rValue);
  98. rDynArray := ctx.GetType(rValue.TypeInfo) as TRTTIDynamicArrayType;
  99. for i := 0 to aJsonArray.Count - 1 do
  100. begin
  101. rItemValue := nil;
  102. case rType.Kind of
  103. tkClass :
  104. begin
  105. if aJsonArray.Items[i] is TJSONObject then
  106. begin
  107. objClass := rType.TypeData.ClassType;
  108. rItemValue := DeserializeClass(objClass, TJSONObject(aJsonArray.Items[i]));
  109. end;
  110. end;
  111. tkRecord :
  112. begin
  113. json := TJSONObject(aJsonArray.Items[i]);
  114. rItemValue := DeserializeRecord(GetValue(PPByte(rValue.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i,
  115. rDynArray.ElementType),aObject,json);
  116. end;
  117. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  118. begin
  119. //skip these properties
  120. end
  121. else
  122. begin
  123. //raise EJsonSerializeError.Create(Format(cNotSupportedDataType,[aProperty.Name,GetTypeName(rType)]));
  124. rItemValue := DeserializeType(aObject,rType.Kind,aProperty.GetValue(aObject).TypeInfo,aJsonArray.Items[i].Value);
  125. end;
  126. end;
  127. if not rItemValue.IsEmpty then rValue.SetArrayElement(i,rItemValue);
  128. end;
  129. aProperty.SetValue(aObject,rValue);
  130. finally
  131. DynArrayClear(pArr, aProperty.PropertyType.Handle);
  132. end;
  133. end;
  134. function TJsonSerializer.DeserializeRecord(aRecord : TValue; aObject : TObject; const aJson : TJSONObject) : TValue;
  135. var
  136. ctx : TRttiContext;
  137. rRec : TRttiRecordType;
  138. rField : TRttiField;
  139. rValue : TValue;
  140. member : TJSONPair;
  141. begin
  142. rRec := ctx.GetType(aRecord.TypeInfo).AsRecord;
  143. for rField in rRec.GetFields do
  144. begin
  145. member := TJSONPair(aJson.GetValue(rField.Name));
  146. if member <> nil then
  147. case rField.FieldType.TypeKind of
  148. tkDynArray :
  149. begin
  150. {jArray := TJSONObject.ParseJSONValue(member.ToJSON) as TJSONArray;
  151. try
  152. DeserializeDynArray(aProp,Result,jArray);
  153. finally
  154. jArray.Free;
  155. end;}
  156. end;
  157. tkClass :
  158. begin
  159. //if (member.JsonValue is TJSONObject) then
  160. {begin
  161. objClass := aProp.PropertyType.Handle^.TypeData.ClassType;
  162. rValue := DeserializeClass(objClass, TJSONObject.ParseJSONValue(member.ToJson) as TJSONObject);
  163. //aProp.SetValue(Result, rValue);
  164. end;}
  165. end;
  166. else
  167. begin
  168. rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.JsonString.ToString);
  169. end;
  170. end;
  171. if not rValue.IsEmpty then rField.SetValue(aRecord.GetReferenceToRawData,rValue);
  172. end;
  173. Result := aRecord;
  174. end;
  175. function TJsonSerializer.JsonToObject(aObject: TObject; const aJson: string): TObject;
  176. var
  177. json: TJSONObject;
  178. begin
  179. json := TJSONObject.ParseJSONValue(aJson,True) as TJSONObject;
  180. try
  181. Result := DeserializeObject(aObject,json);
  182. finally
  183. json.Free;
  184. end;
  185. end;
  186. function TJsonSerializer.JsonToObject(aType: TClass; const aJson: string): TObject;
  187. var
  188. json: TJSONObject;
  189. begin
  190. json := TJSONObject.ParseJSONValue(aJson) as TJSONObject;
  191. try
  192. Result := DeserializeClass(aType,json);
  193. finally
  194. json.Free;
  195. end;
  196. end;
  197. function TJsonSerializer.ObjectToJson(aObject: TObject): string;
  198. var
  199. json: TJSONObject;
  200. begin
  201. json := Serialize(aObject);
  202. try
  203. Result := json.ToJSON;
  204. finally
  205. json.Free;
  206. end;
  207. end;
  208. function TJsonSerializer.DeserializeClass(aType: TClass; const aJson: TJSONObject): TObject;
  209. begin
  210. Result := nil;
  211. if aJson.Count = 0 then Exit;
  212. Result := aType.Create;
  213. try
  214. Result := DeserializeObject(Result,aJson);
  215. except
  216. Result.Free;
  217. raise;
  218. end;
  219. end;
  220. function TJsonSerializer.DeserializeObject(aObject: TObject; const aJson: TJSONObject): TObject;
  221. var
  222. ctx: TRttiContext;
  223. rType: TRttiType;
  224. rProp: TRttiProperty;
  225. attr: TCustomAttribute;
  226. rValue: TValue;
  227. NotSerializable: Boolean;
  228. propertyname : string;
  229. begin
  230. Result := aObject;
  231. if (aJson.Count = 0) or (Result = nil) then Exit;
  232. NotSerializable := True;
  233. try
  234. rType := ctx.GetType(aObject.ClassInfo);
  235. try
  236. for rProp in rType.GetProperties do
  237. begin
  238. if (rProp.PropertyType.IsPublicType) and (rProp.Name <> 'RefCount') then
  239. begin
  240. propertyname := rProp.Name;
  241. for attr in rProp.GetAttributes do if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
  242. rValue := DeserializeObject(Result, propertyname, rProp, aJson);
  243. end;
  244. NotSerializable := False;
  245. end;
  246. finally
  247. ctx.Free;
  248. end;
  249. if NotSerializable then
  250. begin
  251. raise EJsonSerializeError.Create(cNotSerializable);
  252. end;
  253. except
  254. Result.Free;
  255. raise;
  256. end;
  257. end;
  258. function TJsonSerializer.DeserializeObject(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aJson : TJSONObject) : TObject;
  259. var
  260. rType : PTypeInfo;
  261. ctx : TRttiContext;
  262. rValue : TValue;
  263. member : TJSONPair;
  264. NotSerializable : Boolean;
  265. objClass: TClass;
  266. jArray : TJSONArray;
  267. rRec : TRttiRecordType;
  268. rField : TRttiField;
  269. json : TJSONObject;
  270. begin
  271. Result := aObject;
  272. member := TJSONPair(aJson.GetValue(aName));
  273. if member <> nil then
  274. begin
  275. case aProperty.PropertyType.TypeKind of
  276. tkDynArray :
  277. begin
  278. jArray := TJSONObject.ParseJSONValue(member.ToJSON) as TJSONArray;
  279. try
  280. DeserializeDynArray(aProperty,Result,jArray);
  281. finally
  282. jArray.Free;
  283. end;
  284. end;
  285. tkClass :
  286. begin
  287. //if (member.JsonValue is TJSONObject) then
  288. begin
  289. objClass := aProperty.PropertyType.Handle^.TypeData.ClassType;
  290. rValue := DeserializeClass(objClass, TJSONObject.ParseJSONValue(member.ToJson) as TJSONObject);
  291. end
  292. end;
  293. tkRecord :
  294. begin
  295. json := TJSONObject.ParseJSONValue(member.ToJson) as TJSONObject;
  296. try
  297. rValue := DeserializeRecord(aProperty.GetValue(aObject),aObject,json);
  298. finally
  299. json.Free;
  300. end;
  301. end;
  302. else
  303. begin
  304. rValue := DeserializeType(Result,aProperty.PropertyType.TypeKind,aProperty.GetValue(Result).TypeInfo,member.ToJSON);
  305. end;
  306. end;
  307. if not rValue.IsEmpty then aProperty.SetValue(Result,rValue);
  308. end;
  309. end;
  310. function TJsonSerializer.DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
  311. var
  312. i : Integer;
  313. value : string;
  314. begin
  315. value := AnsiDequotedStr(aValue,'"');
  316. case aType of
  317. tkString, tkLString, tkWString, tkUString :
  318. begin
  319. Result := value;
  320. end;
  321. tkChar, tkWChar :
  322. begin
  323. Result := value;
  324. end;
  325. tkInteger :
  326. begin
  327. Result := StrToInt(value);
  328. end;
  329. tkInt64 :
  330. begin
  331. Result := StrToInt64(value);
  332. end;
  333. tkFloat :
  334. begin
  335. if aTypeInfo = TypeInfo(TDateTime) then
  336. begin
  337. Result := JsonDateToDateTime(value);
  338. end
  339. else if aTypeInfo = TypeInfo(TDate) then
  340. begin
  341. Result := StrToDate(value);
  342. end
  343. else if aTypeInfo = TypeInfo(TTime) then
  344. begin
  345. Result := StrToTime(value);
  346. end
  347. else
  348. begin
  349. Result := StrToFloat(value);
  350. end;
  351. end;
  352. tkEnumeration :
  353. begin
  354. if aTypeInfo = System.TypeInfo(Boolean) then
  355. begin
  356. Result := StrToBool(value);
  357. end
  358. else
  359. begin
  360. TValue.Make(GetEnumValue(aTypeInfo,value),aTypeInfo, Result);
  361. end;
  362. end;
  363. tkSet :
  364. begin
  365. i := StringToSet(aTypeInfo,value);
  366. TValue.Make(@i,aTypeInfo,Result);
  367. end;
  368. else
  369. begin
  370. //raise EclJsonSerializerError.Create('Not supported data type!');
  371. end;
  372. end;
  373. end;
  374. function TJsonSerializer.Serialize(aObject: TObject): TJSONObject;
  375. var
  376. ctx: TRttiContext;
  377. attr : TCustomAttribute;
  378. rType: TRttiType;
  379. rProp: TRttiProperty;
  380. NotSerializable: Boolean;
  381. jpair : TJSONPair;
  382. ExcludeSerialize : Boolean;
  383. comment : string;
  384. propertyname : string;
  385. begin
  386. if (aObject = nil) then
  387. begin
  388. Result := nil;
  389. Exit;
  390. end;
  391. NotSerializable := True;
  392. Result := TJSONObject.Create;
  393. try
  394. rType := ctx.GetType(aObject.ClassInfo);
  395. try
  396. //s := rType.ToString;
  397. for rProp in rType.GetProperties do
  398. begin
  399. ExcludeSerialize := False;
  400. comment := '';
  401. propertyname := rProp.Name;
  402. for attr in rProp.GetAttributes do
  403. begin
  404. if attr is TNotSerializableProperty then ExcludeSerialize := True
  405. else if attr is TCommentProperty then comment := TCommentProperty(attr).Comment
  406. else if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
  407. end;
  408. if (rProp.PropertyType.IsPublicType) and (rProp.Name <> 'RefCount') and (not ExcludeSerialize) then
  409. begin
  410. //add comment as pair
  411. if comment <> '' then Result.AddPair(TJSONPair.Create('#Comment#->'+propertyname,Comment));
  412. //s := rProp.Name;
  413. jpair := Serialize(propertyname,rProp.GetValue(aObject));
  414. //s := jpair.JsonValue.ToString;
  415. if jpair <> nil then Result.AddPair(jpair)
  416. else jpair.Free;
  417. //Result.AddPair(Serialize(rProp.Name,rProp.GetValue(aObject)));
  418. //s := Result.ToJSON;
  419. end;
  420. NotSerializable := False;
  421. end;
  422. finally
  423. ctx.Free;
  424. end;
  425. if NotSerializable then
  426. begin
  427. raise EJsonSerializeError.Create(cNotSerializable);
  428. end;
  429. except
  430. Result.Free;
  431. raise;
  432. end;
  433. end;
  434. function TJsonSerializer.GetValue(aAddr: Pointer; aType: TRTTIType): TValue;
  435. begin
  436. TValue.Make(aAddr,aType.Handle,Result);
  437. end;
  438. function TJsonSerializer.Serialize(const aName : string; aValue : TValue): TJSONPair;
  439. var
  440. ctx: TRttiContext;
  441. rRec : TRttiRecordType;
  442. rField : TRttiField;
  443. rDynArray : TRTTIDynamicArrayType;
  444. json : TJSONObject;
  445. jArray : TJSONArray;
  446. jPair : TJSONPair;
  447. jValue : TJSONValue;
  448. i : Integer;
  449. begin
  450. Result := TJSONPair.Create(aName,nil);
  451. //Result.JsonString := TJSONString(aName);
  452. try
  453. case aValue.Kind of
  454. tkDynArray :
  455. begin
  456. jArray := TJSONArray.Create;
  457. rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
  458. try
  459. for i := 0 to aValue.GetArrayLength - 1 do
  460. begin
  461. jPair := Serialize(aName,GetValue(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType));
  462. try
  463. //jValue := TJsonValue(jPair.JsonValue.Clone);
  464. jValue := jPair.JsonValue;
  465. jArray.AddElement(jValue);
  466. jPair.JsonValue.Owned := False;
  467. finally
  468. jPair.Free;
  469. jValue.Owned := True;
  470. end;
  471. end;
  472. Result.JsonValue := jArray;
  473. finally
  474. ctx.Free;
  475. end;
  476. end;
  477. tkClass :
  478. begin
  479. Result.JsonValue := TJSONValue(Serialize(aValue.AsObject));
  480. end;
  481. tkString, tkLString, tkWString, tkUString :
  482. begin
  483. Result.JsonValue := TJSONString.Create(aValue.AsString);
  484. end;
  485. tkChar, tkWChar :
  486. begin
  487. Result.JsonValue := TJSONString.Create(aValue.AsString);
  488. end;
  489. tkInteger :
  490. begin
  491. Result.JsonValue := TJSONNumber.Create(aValue.AsInteger);
  492. end;
  493. tkInt64 :
  494. begin
  495. Result.JsonValue := TJSONNumber.Create(aValue.AsInt64);
  496. end;
  497. tkFloat :
  498. begin
  499. if aValue.TypeInfo = TypeInfo(TDateTime) then
  500. begin
  501. Result.JsonValue := TJSONString.Create(DateTimeToJsonDate(aValue.AsExtended));
  502. end
  503. else if aValue.TypeInfo = TypeInfo(TDate) then
  504. begin
  505. Result.JsonValue := TJSONString.Create(DateToStr(aValue.AsExtended));
  506. end
  507. else if aValue.TypeInfo = TypeInfo(TTime) then
  508. begin
  509. Result.JsonValue := TJSONString.Create(TimeToStr(aValue.AsExtended));
  510. end
  511. else Result.JsonValue := TJSONNumber.Create(aValue.AsExtended);
  512. end;
  513. tkEnumeration :
  514. begin
  515. if (aValue.TypeInfo = System.TypeInfo(Boolean)) then
  516. begin
  517. Result.JsonValue := TJSONBool.Create(aValue.AsBoolean);
  518. end
  519. else
  520. begin
  521. Result.JsonValue := TJSONString.Create(GetEnumName(aValue.TypeInfo,aValue.AsOrdinal));
  522. //Result.JsonValue := TJSONString.Create(aValue.ToString);
  523. end;
  524. end;
  525. tkSet :
  526. begin
  527. Result.JsonValue := TJSONString.Create(aValue.ToString);
  528. end;
  529. tkRecord :
  530. begin
  531. rRec := ctx.GetType(aValue.TypeInfo).AsRecord;
  532. try
  533. json := TJSONObject.Create;
  534. for rField in rRec.GetFields do
  535. begin
  536. json.AddPair(Serialize(rField.name,rField.GetValue(aValue.GetReferenceToRawData)));
  537. end;
  538. Result.JsonValue := json;
  539. finally
  540. ctx.Free;
  541. end;
  542. end;
  543. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  544. begin
  545. //skip these properties
  546. FreeAndNil(Result);
  547. end
  548. else
  549. begin
  550. raise EJsonSerializeError.Create(Format(cNotSupportedDataType,[aName,GetTypeName(aValue.TypeInfo)]));
  551. end;
  552. end;
  553. except
  554. Result.Free;
  555. raise;
  556. end;
  557. end;
  558. { TCommentProperty }
  559. constructor TCommentProperty.Create(const aComment: string);
  560. begin
  561. fComment := aComment;
  562. end;
  563. { TCustomNameProperty }
  564. constructor TCustomNameProperty.Create(const aName: string);
  565. begin
  566. fName := aName;
  567. end;
  568. end.