Quick.Json.Serializer.pas 20 KB

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