Quick.Json.Serializer.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  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 : TRttiRecordType; aTypeInfo : PTypeInfo; 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. s : string;
  89. begin
  90. if GetTypeData(aProperty.PropertyType.Handle).DynArrElType = nil then Exit;
  91. len := aJsonArray.Count;
  92. rType := GetTypeData(aProperty.PropertyType.Handle).DynArrElType^;
  93. pArr := nil;
  94. DynArraySetLength(pArr, aProperty.PropertyType.Handle, 1, @len);
  95. try
  96. TValue.Make(@pArr, aProperty.PropertyType.Handle, rValue);
  97. for i := 0 to aJsonArray.Count - 1 do
  98. begin
  99. rItemValue := nil;
  100. case rType.Kind of
  101. tkClass :
  102. begin
  103. if aJsonArray.Items[i] is TJSONObject then
  104. begin
  105. objClass := rType.TypeData.ClassType;
  106. rItemValue := DeserializeClass(objClass, TJSONObject(aJsonArray.Items[i]));
  107. end;
  108. end;
  109. tkRecord :
  110. begin
  111. {rRec := ctx.GetType(rType).AsRecord;
  112. try
  113. rItemValue := DeserializeRecord(rRec,aObject,TJSONObject(aJsonArray.Items[i]));
  114. finally
  115. ctx.Free;
  116. end;}
  117. end;
  118. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  119. begin
  120. //skip these properties
  121. end
  122. else
  123. begin
  124. //raise EJsonSerializeError.Create(Format(cNotSupportedDataType,[aProperty.Name,GetTypeName(rType)]));
  125. rItemValue := DeserializeType(aObject,rType.Kind,aProperty.GetValue(aObject).TypeInfo,aJsonArray.Items[i].Value);
  126. end;
  127. end;
  128. if not rItemValue.IsEmpty then rValue.SetArrayElement(i,rItemValue);
  129. end;
  130. aProperty.SetValue(aObject,rValue);
  131. finally
  132. DynArrayClear(pArr, aProperty.PropertyType.Handle);
  133. end;
  134. end;
  135. function TJsonSerializer.DeserializeRecord(aRecord : TRttiRecordType; aTypeInfo : PTypeInfo; aObject : TObject; const aJson : TJSONObject) : TValue;
  136. var
  137. rField : TRttiField;
  138. rValue : TValue;
  139. member : TJSONPair;
  140. s : string;
  141. begin
  142. TValue.Make(@aRecord,aTypeInfo,Result);
  143. for rField in aRecord.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. s := member.JsonString.ToString;
  169. rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.JsonString.ToString);
  170. end;
  171. end;
  172. rField.SetValue(@aRecord,rValue);
  173. end;
  174. //Result := aRecord;
  175. end;
  176. function TJsonSerializer.JsonToObject(aObject: TObject; const aJson: string): TObject;
  177. var
  178. json: TJSONObject;
  179. begin
  180. json := TJSONObject.ParseJSONValue(aJson,True) as TJSONObject;
  181. try
  182. Result := DeserializeObject(aObject,json);
  183. finally
  184. json.Free;
  185. end;
  186. end;
  187. function TJsonSerializer.JsonToObject(aType: TClass; const aJson: string): TObject;
  188. var
  189. json: TJSONObject;
  190. begin
  191. json := TJSONObject.ParseJSONValue(aJson) as TJSONObject;
  192. try
  193. Result := DeserializeClass(aType,json);
  194. finally
  195. json.Free;
  196. end;
  197. end;
  198. function TJsonSerializer.ObjectToJson(aObject: TObject): string;
  199. var
  200. json: TJSONObject;
  201. begin
  202. json := Serialize(aObject);
  203. try
  204. Result := json.ToJSON;
  205. finally
  206. json.Free;
  207. end;
  208. end;
  209. function TJsonSerializer.DeserializeClass(aType: TClass; const aJson: TJSONObject): TObject;
  210. begin
  211. Result := nil;
  212. if aJson.Count = 0 then Exit;
  213. Result := aType.Create;
  214. try
  215. Result := DeserializeObject(Result,aJson);
  216. except
  217. Result.Free;
  218. raise;
  219. end;
  220. end;
  221. function TJsonSerializer.DeserializeObject(aObject: TObject; const aJson: TJSONObject): TObject;
  222. var
  223. ctx: TRttiContext;
  224. rType: TRttiType;
  225. rProp: TRttiProperty;
  226. attr: TCustomAttribute;
  227. rValue: TValue;
  228. NotSerializable: Boolean;
  229. propertyname : string;
  230. begin
  231. Result := aObject;
  232. if (aJson.Count = 0) or (Result = nil) then Exit;
  233. NotSerializable := True;
  234. try
  235. rType := ctx.GetType(aObject.ClassInfo);
  236. try
  237. for rProp in rType.GetProperties do
  238. begin
  239. if (rProp.PropertyType.IsPublicType) and (rProp.Name <> 'RefCount') then
  240. begin
  241. propertyname := rProp.Name;
  242. for attr in rProp.GetAttributes do if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
  243. rValue := DeserializeObject(Result, propertyname, rProp, aJson);
  244. //rProp.SetValue(Result,rValue);
  245. end;
  246. NotSerializable := False;
  247. end;
  248. finally
  249. ctx.Free;
  250. end;
  251. if NotSerializable then
  252. begin
  253. raise EJsonSerializeError.Create(cNotSerializable);
  254. end;
  255. except
  256. Result.Free;
  257. raise;
  258. end;
  259. end;
  260. function TJsonSerializer.DeserializeObject(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aJson : TJSONObject) : TObject;
  261. var
  262. rType : PTypeInfo;
  263. ctx : TRttiContext;
  264. rValue : TValue;
  265. member : TJSONPair;
  266. NotSerializable : Boolean;
  267. objClass: TClass;
  268. jArray : TJSONArray;
  269. rRec : TRttiRecordType;
  270. rField : TRttiField;
  271. begin
  272. Result := aObject;
  273. member := TJSONPair(aJson.GetValue(aName));
  274. if member <> nil then
  275. begin
  276. case aProperty.PropertyType.TypeKind of
  277. tkDynArray :
  278. begin
  279. jArray := TJSONObject.ParseJSONValue(member.ToJSON) as TJSONArray;
  280. try
  281. DeserializeDynArray(aProperty,Result,jArray);
  282. finally
  283. jArray.Free;
  284. end;
  285. end;
  286. tkClass :
  287. begin
  288. //if (member.JsonValue is TJSONObject) then
  289. begin
  290. objClass := aProperty.PropertyType.Handle^.TypeData.ClassType;
  291. rValue := DeserializeClass(objClass, TJSONObject.ParseJSONValue(member.ToJson) as TJSONObject);
  292. end
  293. end;
  294. tkRecord :
  295. begin
  296. {rRec := ctx.GetType(aProperty.PropertyType.Handle).AsRecord;
  297. //rValue := GetValue(aProp.Handle,TRttiRecordType);
  298. //TValue.Make(@rRec,aProp.PropertyType.Handle, rValue);
  299. //rRec.GetField('IP').SetValue(@rRec,'127.0.0.2');
  300. //rRec.GetField('MsgSize').SetValue(@rRec,1024);
  301. try
  302. rValue := DeserializeRecord(rRec,aProperty.GetValue(aObject).TypeInfo,aObject,TJSONObject.ParseJSONValue(member.ToJson) as TJSONObject);
  303. finally
  304. ctx.Free;
  305. end;}
  306. end;
  307. else
  308. begin
  309. rValue := DeserializeType(Result,aProperty.PropertyType.TypeKind,aProperty.GetValue(Result).TypeInfo,member.ToJSON);
  310. end;
  311. end;
  312. if not rValue.IsEmpty then aProperty.SetValue(Result,rValue);
  313. end;
  314. end;
  315. function TJsonSerializer.DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
  316. var
  317. i : Integer;
  318. value : string;
  319. begin
  320. value := AnsiDequotedStr(aValue,'"');
  321. case aType of
  322. tkString, tkLString, tkWString, tkUString :
  323. begin
  324. Result := value;
  325. end;
  326. tkChar, tkWChar :
  327. begin
  328. Result := value;
  329. end;
  330. tkInteger :
  331. begin
  332. Result := StrToInt(value);
  333. end;
  334. tkInt64 :
  335. begin
  336. Result := StrToInt64(value);
  337. end;
  338. tkFloat :
  339. begin
  340. if aTypeInfo = TypeInfo(TDateTime) then
  341. begin
  342. Result := JsonDateToDateTime(value);
  343. end
  344. else if aTypeInfo = TypeInfo(TDate) then
  345. begin
  346. Result := StrToDate(value);
  347. end
  348. else if aTypeInfo = TypeInfo(TTime) then
  349. begin
  350. Result := StrToTime(value);
  351. end
  352. else
  353. begin
  354. Result := StrToFloat(value);
  355. end;
  356. end;
  357. tkEnumeration :
  358. begin
  359. if aTypeInfo = System.TypeInfo(Boolean) then
  360. begin
  361. Result := StrToBool(value);
  362. end
  363. else
  364. begin
  365. TValue.Make(GetEnumValue(aTypeInfo,value),aTypeInfo, Result);
  366. end;
  367. end;
  368. tkSet :
  369. begin
  370. i := StringToSet(aTypeInfo,value);
  371. TValue.Make(@i,aTypeInfo,Result);
  372. end;
  373. else
  374. begin
  375. //raise EclJsonSerializerError.Create('Not supported data type!');
  376. end;
  377. end;
  378. end;
  379. function TJsonSerializer.Serialize(aObject: TObject): TJSONObject;
  380. var
  381. ctx: TRttiContext;
  382. attr : TCustomAttribute;
  383. rType: TRttiType;
  384. rProp: TRttiProperty;
  385. NotSerializable: Boolean;
  386. jpair : TJSONPair;
  387. ExcludeSerialize : Boolean;
  388. comment : string;
  389. propertyname : string;
  390. begin
  391. if (aObject = nil) then
  392. begin
  393. Result := nil;
  394. Exit;
  395. end;
  396. NotSerializable := True;
  397. Result := TJSONObject.Create;
  398. try
  399. rType := ctx.GetType(aObject.ClassInfo);
  400. try
  401. //s := rType.ToString;
  402. for rProp in rType.GetProperties do
  403. begin
  404. ExcludeSerialize := False;
  405. comment := '';
  406. propertyname := rProp.Name;
  407. for attr in rProp.GetAttributes do
  408. begin
  409. if attr is TNotSerializableProperty then ExcludeSerialize := True
  410. else if attr is TCommentProperty then comment := TCommentProperty(attr).Comment
  411. else if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
  412. end;
  413. if (rProp.PropertyType.IsPublicType) and (rProp.Name <> 'RefCount') and (not ExcludeSerialize) then
  414. begin
  415. //add comment as pair
  416. if comment <> '' then Result.AddPair(TJSONPair.Create('#Comment#->'+propertyname,Comment));
  417. //s := rProp.Name;
  418. jpair := Serialize(propertyname,rProp.GetValue(aObject));
  419. //s := jpair.JsonValue.ToString;
  420. if jpair <> nil then Result.AddPair(jpair)
  421. else jpair.Free;
  422. //Result.AddPair(Serialize(rProp.Name,rProp.GetValue(aObject)));
  423. //s := Result.ToJSON;
  424. end;
  425. NotSerializable := False;
  426. end;
  427. finally
  428. ctx.Free;
  429. end;
  430. if NotSerializable then
  431. begin
  432. raise EJsonSerializeError.Create(cNotSerializable);
  433. end;
  434. except
  435. Result.Free;
  436. raise;
  437. end;
  438. end;
  439. function TJsonSerializer.GetValue(aAddr: Pointer; aType: TRTTIType): TValue;
  440. begin
  441. TValue.Make(aAddr,aType.Handle,Result);
  442. end;
  443. function TJsonSerializer.Serialize(const aName : string; aValue : TValue): TJSONPair;
  444. var
  445. ctx: TRttiContext;
  446. rRec : TRttiRecordType;
  447. rField : TRttiField;
  448. rDynArray : TRTTIDynamicArrayType;
  449. json : TJSONObject;
  450. jArray : TJSONArray;
  451. jPair : TJSONPair;
  452. jValue : TJSONValue;
  453. i : Integer;
  454. begin
  455. Result := TJSONPair.Create(aName,nil);
  456. //Result.JsonString := TJSONString(aName);
  457. try
  458. case aValue.Kind of
  459. tkDynArray :
  460. begin
  461. jArray := TJSONArray.Create;
  462. rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
  463. try
  464. for i := 0 to aValue.GetArrayLength - 1 do
  465. begin
  466. jPair := Serialize(aName,GetValue(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType));
  467. try
  468. //jValue := TJsonValue(jPair.JsonValue.Clone);
  469. jValue := jPair.JsonValue;
  470. jArray.AddElement(jValue);
  471. jPair.JsonValue.Owned := False;
  472. finally
  473. jPair.Free;
  474. jValue.Owned := True;
  475. end;
  476. end;
  477. Result.JsonValue := jArray;
  478. finally
  479. ctx.Free;
  480. end;
  481. end;
  482. tkClass :
  483. begin
  484. Result.JsonValue := TJSONValue(Serialize(aValue.AsObject));
  485. end;
  486. tkString, tkLString, tkWString, tkUString :
  487. begin
  488. Result.JsonValue := TJSONString.Create(aValue.AsString);
  489. end;
  490. tkChar, tkWChar :
  491. begin
  492. Result.JsonValue := TJSONString.Create(aValue.AsString);
  493. end;
  494. tkInteger :
  495. begin
  496. Result.JsonValue := TJSONNumber.Create(aValue.AsInteger);
  497. end;
  498. tkInt64 :
  499. begin
  500. Result.JsonValue := TJSONNumber.Create(aValue.AsInt64);
  501. end;
  502. tkFloat :
  503. begin
  504. if aValue.TypeInfo = TypeInfo(TDateTime) then
  505. begin
  506. Result.JsonValue := TJSONString.Create(DateTimeToJsonDate(aValue.AsExtended));
  507. end
  508. else if aValue.TypeInfo = TypeInfo(TDate) then
  509. begin
  510. Result.JsonValue := TJSONString.Create(DateToStr(aValue.AsExtended));
  511. end
  512. else if aValue.TypeInfo = TypeInfo(TTime) then
  513. begin
  514. Result.JsonValue := TJSONString.Create(TimeToStr(aValue.AsExtended));
  515. end
  516. else Result.JsonValue := TJSONNumber.Create(aValue.AsExtended);
  517. end;
  518. tkEnumeration :
  519. begin
  520. if (aValue.TypeInfo = System.TypeInfo(Boolean)) then
  521. begin
  522. Result.JsonValue := TJSONBool.Create(aValue.AsBoolean);
  523. end
  524. else
  525. begin
  526. Result.JsonValue := TJSONString.Create(GetEnumName(aValue.TypeInfo,aValue.AsOrdinal));
  527. //Result.JsonValue := TJSONString.Create(aValue.ToString);
  528. end;
  529. end;
  530. tkSet :
  531. begin
  532. Result.JsonValue := TJSONString.Create(aValue.ToString);
  533. end;
  534. tkRecord :
  535. begin
  536. rRec := ctx.GetType(aValue.TypeInfo).AsRecord;
  537. try
  538. json := TJSONObject.Create;
  539. for rField in rRec.GetFields do
  540. begin
  541. json.AddPair(Serialize(rField.name,rField.GetValue(aValue.GetReferenceToRawData)));
  542. end;
  543. Result.JsonValue := json;
  544. finally
  545. ctx.Free;
  546. end;
  547. end;
  548. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  549. begin
  550. //skip these properties
  551. FreeAndNil(Result);
  552. end
  553. else
  554. begin
  555. raise EJsonSerializeError.Create(Format(cNotSupportedDataType,[aName,GetTypeName(aValue.TypeInfo)]));
  556. end;
  557. end;
  558. except
  559. Result.Free;
  560. raise;
  561. end;
  562. end;
  563. { TCommentProperty }
  564. constructor TCommentProperty.Create(const aComment: string);
  565. begin
  566. fComment := aComment;
  567. end;
  568. { TCustomNameProperty }
  569. constructor TCustomNameProperty.Create(const aName: string);
  570. begin
  571. fName := aName;
  572. end;
  573. end.