Quick.YAML.Serializer.pas 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570
  1. { ***************************************************************************
  2. Copyright (c) 2015-2019 Kike Pérez
  3. Unit : Quick.YAML.Serializer
  4. Description : YAML Serializer
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 12/04/2019
  8. Modified : 10/12/2019
  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.YAML.Serializer;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. SysUtils,
  27. Rtti,
  28. TypInfo,
  29. {$IFDEF FPC}
  30. rttiutils,
  31. strUtils,
  32. Generics.Collections,
  33. {$ELSE}
  34. {$IFDEF DELPHIRX103_UP}
  35. System.Generics.Collections,
  36. {$ENDIF}
  37. {$ENDIF}
  38. DateUtils,
  39. Quick.Commons,
  40. Quick.YAML,
  41. Quick.Value,
  42. Quick.Arrays;
  43. type
  44. EYamlSerializeError = class(Exception);
  45. EYamlDeserializeError = class(Exception);
  46. {$IFNDEF FPC}
  47. TNotSerializableProperty = class(TCustomAttribute);
  48. TCommentProperty = class(TCustomAttribute)
  49. private
  50. fComment : string;
  51. public
  52. constructor Create(const aComment: string);
  53. property Comment : string read fComment;
  54. end;
  55. TCustomNameProperty = class(TCustomAttribute)
  56. private
  57. fName : string;
  58. public
  59. constructor Create(const aName: string);
  60. property Name : string read fName;
  61. end;
  62. {$ENDIF}
  63. IYamlSerializer = interface
  64. ['{CA26F7AE-F1FE-41BE-9C23-723A687F60D1}']
  65. function YamlToObject(aType: TClass; const aYaml: string): TObject; overload;
  66. function YamlToObject(aObject: TObject; const aYaml: string): TObject; overload;
  67. function ObjectToYaml(aObject : TObject): string;
  68. end;
  69. TSerializeLevel = (slPublicProperty, slPublishedProperty);
  70. TRTTIYaml = class
  71. private
  72. fSerializeLevel : TSerializeLevel;
  73. fUseEnumNames : Boolean;
  74. fUseYamlCaseSense : Boolean;
  75. function GetValue(aAddr: Pointer; aType: TRTTIType): TValue; overload;
  76. function GetValue(aAddr: Pointer; aTypeInfo: PTypeInfo): TValue; overload;
  77. function IsAllowedProperty(aObject : TObject; const aPropertyName : string) : Boolean;
  78. function IsGenericList(aObject : TObject) : Boolean;
  79. function IsGenericXArray(const aClassName : string) : Boolean;
  80. function GetPropertyValue(Instance : TObject; const PropertyName : string) : TValue;
  81. function GetPropertyValueFromObject(Instance : TObject; const PropertyName : string) : TValue;
  82. {$IFNDEF FPC}
  83. function GetFieldValueFromRecord(aValue : TValue; const FieldName : string) : TValue;
  84. {$ENDIF}
  85. procedure SetPropertyValue(Instance : TObject; aPropInfo : PPropInfo; aValue : TValue); overload;
  86. procedure SetPropertyValue(Instance : TObject; const PropertyName : string; aValue : TValue); overload;
  87. {$IFDEF FPC}
  88. function FloatProperty(aObject : TObject; aPropInfo: PPropInfo): string;
  89. function GetPropType(aPropInfo: PPropInfo): PTypeInfo;
  90. procedure LoadSetProperty(aInstance : TObject; aPropInfo: PPropInfo; const aValue: string);
  91. {$ENDIF}
  92. public
  93. constructor Create(aSerializeLevel : TSerializeLevel; aUseEnumNames : Boolean = True);
  94. property UseEnumNames : Boolean read fUseEnumNames write fUseEnumNames;
  95. property UseYamlCaseSense : Boolean read fUseYamlCaseSense write fUseYamlCaseSense;
  96. {$IFNDEF FPC}
  97. function DeserializeDynArray(aTypeInfo : PTypeInfo; aObject : TObject; const aYamlArray: TYamlArray) : TValue;
  98. function DeserializeRecord(aRecord : TValue; aObject : TObject; const aYaml : TYamlObject) : TValue;
  99. {$ELSE}
  100. procedure DeserializeDynArray(aTypeInfo: PTypeInfo; const aPropertyName : string; aObject: TObject; const aYamlArray: TYamlArray);
  101. {$ENDIF}
  102. function DeserializeClass(aType : TClass; const aYaml : TYamlObject) : TObject;
  103. function DeserializeObject(aObject : TObject; const aYaml : TYamlObject) : TObject; overload;
  104. {$IFNDEF FPC}
  105. function DeserializeList(aObject: TObject; const aName : string; const aYaml: TYamlObject) : TObject;
  106. procedure DeserializeXArray(Instance : TObject; aRecord : TValue; aProperty : TRttiProperty; const aPropertyName : string; aYaml : TYamlObject);
  107. {$ENDIF}
  108. function DeserializeProperty(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aYaml : TYamlObject) : TObject; overload;
  109. {$IFNDEF FPC}
  110. function DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
  111. {$ELSE}
  112. function DeserializeType(aObject : TObject; aType : TTypeKind; const aPropertyName, aValue: string) : TValue;
  113. {$ENDIF}
  114. {$IFNDEF FPC}
  115. function Serialize(const aName : string; aValue : TValue) : TYamlPair; overload;
  116. {$ELSE}
  117. function Serialize(aObject : TObject; aType : TTypeKind; const aPropertyName : string) : TYamlPair;
  118. function Serialize(const aName : string; aValue : TValue) : TYamlPair;
  119. {$ENDIF}
  120. function Serialize(aObject : TObject) : TYamlObject; overload;
  121. function GetYamlPairByName(aYaml : TYamlObject; const aName : string) : TYamlPair;
  122. end;
  123. TYamlSerializer = class(TInterfacedObject,IYamlSerializer)
  124. strict private
  125. fSerializeLevel : TSerializeLevel;
  126. fUseEnumNames : Boolean;
  127. fUseYamlCaseSense : Boolean;
  128. fRTTIYaml : TRTTIYaml;
  129. private
  130. procedure SetUseEnumNames(const Value: Boolean);
  131. procedure SetUseYamlCaseSense(const Value: Boolean);
  132. public
  133. constructor Create(aSerializeLevel: TSerializeLevel; aUseEnumNames : Boolean = True);
  134. destructor Destroy; override;
  135. property SerializeLevel : TSerializeLevel read fSerializeLevel;
  136. property UseEnumNames : Boolean read fUseEnumNames write SetUseEnumNames;
  137. property UseYamlCaseSense : Boolean read fUseYamlCaseSense write SetUseYamlCaseSense;
  138. function YamlToObject(aType : TClass; const aYaml: string) : TObject; overload;
  139. function YamlToObject(aObject : TObject; const aYaml: string) : TObject; overload;
  140. function ObjectToYaml(aObject : TObject): string;
  141. end;
  142. PPByte = ^PByte;
  143. resourcestring
  144. cNotSupportedDataType = 'Not supported "%s" data type "%s"';
  145. cNotSerializable = 'Object is not serializable';
  146. implementation
  147. { TRTTIYaml }
  148. {$IFNDEF FPC}
  149. function TRTTIYaml.DeserializeDynArray(aTypeInfo: PTypeInfo; aObject: TObject; const aYamlArray: TYamlArray) : TValue;
  150. var
  151. rType: PTypeInfo;
  152. len: NativeInt;
  153. pArr: Pointer;
  154. rItemValue: TValue;
  155. i: Integer;
  156. objClass: TClass;
  157. ctx : TRttiContext;
  158. Yaml : TYamlObject;
  159. rDynArray : TRttiDynamicArrayType;
  160. propObj : TObject;
  161. begin
  162. if GetTypeData(aTypeInfo).DynArrElType = nil then Exit;
  163. if not assigned(aYamlArray) then Exit;
  164. len := aYamlArray.Count;
  165. rType := GetTypeData(aTypeInfo).DynArrElType^;
  166. pArr := nil;
  167. DynArraySetLength(pArr,aTypeInfo, 1, @len);
  168. try
  169. TValue.Make(@pArr,aTypeInfo, Result);
  170. rDynArray := ctx.GetType(Result.TypeInfo) as TRTTIDynamicArrayType;
  171. for i := 0 to aYamlArray.Count - 1 do
  172. begin
  173. rItemValue := nil;
  174. case rType.Kind of
  175. tkClass :
  176. begin
  177. if TYamlPair(aYamlArray.Items[i]).Value is TYamlObject then
  178. begin
  179. Yaml := TYamlObject(TYamlPair(aYamlArray.Items[i]).value);
  180. propObj := GetValue(PPByte(Result.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i, rDynArray.ElementType).AsObject;
  181. if propObj = nil then
  182. begin
  183. objClass := rType.TypeData.ClassType;
  184. rItemValue := DeserializeClass(objClass,yaml);
  185. end
  186. else
  187. begin
  188. DeserializeObject(propObj,yaml);
  189. end;
  190. end;
  191. end;
  192. tkRecord :
  193. begin
  194. Yaml := TYamlObject(TYamlPair(aYamlArray.Items[i]).value);
  195. rItemValue := DeserializeRecord(GetValue(PPByte(Result.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i,
  196. rDynArray.ElementType),aObject,Yaml);
  197. end;
  198. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  199. begin
  200. //skip these properties
  201. end
  202. else
  203. begin
  204. rItemValue := DeserializeType(aObject,rType.Kind,aTypeInfo,aYamlArray.Items[i].Value);
  205. end;
  206. end;
  207. if not rItemValue.IsEmpty then Result.SetArrayElement(i,rItemValue);
  208. end;
  209. //aProperty.SetValue(aObject,rValue);
  210. finally
  211. DynArrayClear(pArr,aTypeInfo);
  212. end;
  213. end;
  214. {$ELSE}
  215. procedure TRTTIYaml.DeserializeDynArray(aTypeInfo: PTypeInfo; const aPropertyName : string; aObject: TObject; const aYamlArray: TYamlArray);
  216. var
  217. rType: PTypeInfo;
  218. len: NativeInt;
  219. pArr: Pointer;
  220. rItemValue: TValue;
  221. i: Integer;
  222. objClass: TClass;
  223. propObj : TObject;
  224. rValue : TValue;
  225. yaml : TYamlObject;
  226. begin
  227. if GetTypeData(aTypeInfo).ElType2 = nil then Exit;
  228. len := aYamlArray.Count;
  229. rType := GetTypeData(aTypeInfo).ElType2;
  230. pArr := nil;
  231. DynArraySetLength(pArr,aTypeInfo, 1, @len);
  232. try
  233. TValue.Make(@pArr,aTypeInfo, rValue);
  234. for i := 0 to aYamlArray.Count - 1 do
  235. begin
  236. rItemValue := nil;
  237. case rType.Kind of
  238. tkClass :
  239. begin
  240. if TYamlPair(aYamlArray.Items[i]).Value is TYamlObject then
  241. begin
  242. Yaml := TYamlObject(TYamlPair(aYamlArray.Items[i]).value);
  243. propObj := GetValue(PPByte(rValue.GetReferenceToRawData)^ +GetTypeData(aTypeInfo).elSize * i, GetTypeData(aTypeInfo).ElType2).AsObject;
  244. if propObj = nil then
  245. begin
  246. //objClass := GetTypeData(aTypeInfo).ClassType;
  247. objClass := GetTypeData(GetTypeData(aTypeInfo).ElType2).ClassType;
  248. rItemValue := DeserializeClass(objClass,yaml);
  249. end
  250. else
  251. begin
  252. DeserializeObject(propObj,yaml);
  253. end;
  254. end;
  255. end;
  256. tkRecord :
  257. begin
  258. {Yaml := TYamlObject(aYamlArray.Items[i]);
  259. rItemValue := DeserializeRecord(GetValue(PPByte(Result.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i,
  260. rDynArray.ElementType),aObject,Yaml); }
  261. end;
  262. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  263. begin
  264. //skip these properties
  265. end
  266. else
  267. begin
  268. rItemValue := DeserializeType(aObject,GetTypeData(aTypeInfo).ElType2.Kind,aPropertyName,aYamlArray.Items[i].Value);
  269. end;
  270. end;
  271. if not rItemValue.IsEmpty then rValue.SetArrayElement(i,rItemValue);
  272. end;
  273. //aProperty.SetValue(aObject,rValue);
  274. SetDynArrayProp(aObject,GetPropInfo(aObject,aPropertyName),pArr);
  275. finally
  276. DynArrayClear(pArr,aTypeInfo);
  277. end;
  278. end;
  279. {$ENDIF}
  280. {$IFNDEF FPC}
  281. function TRTTIYaml.DeserializeRecord(aRecord : TValue; aObject : TObject; const aYaml : TYamlObject) : TValue;
  282. var
  283. ctx : TRttiContext;
  284. rRec : TRttiRecordType;
  285. rField : TRttiField;
  286. rValue : TValue;
  287. member : TYamlPair;
  288. yArray : TYamlArray;
  289. Yaml : TYamlObject;
  290. objClass : TClass;
  291. propobj : TObject;
  292. begin
  293. rRec := ctx.GetType(aRecord.TypeInfo).AsRecord;
  294. try
  295. for rField in rRec.GetFields do
  296. begin
  297. rValue := nil;
  298. //member := TYamlPair(aYaml.GetValue(rField.Name));
  299. member := GetYamlPairByName(aYaml,rField.Name);
  300. if member <> nil then
  301. case rField.FieldType.TypeKind of
  302. tkDynArray :
  303. begin
  304. yArray := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlArray;
  305. try
  306. rValue := DeserializeDynArray(rField.FieldType.Handle,aObject,yArray);
  307. finally
  308. yArray.Free;
  309. end;
  310. end;
  311. tkClass :
  312. begin
  313. //if (member.YamlValue is TYamlObject) then
  314. begin
  315. propobj := rField.GetValue(@aRecord).AsObject;
  316. Yaml := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlObject;
  317. try
  318. if propobj = nil then
  319. begin
  320. objClass := rField.FieldType.Handle^.TypeData.ClassType;// aProperty.PropertyType.Handle^.TypeData.ClassType;
  321. rValue := DeserializeClass(objClass,Yaml);
  322. end
  323. else
  324. begin
  325. DeserializeObject(propobj,Yaml);
  326. end;
  327. finally
  328. Yaml.Free;
  329. end;
  330. end
  331. end;
  332. tkRecord :
  333. begin
  334. Yaml := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlObject;
  335. try
  336. rValue := DeserializeRecord(rField.GetValue(aRecord.GetReferenceToRawData),aObject,Yaml);
  337. finally
  338. Yaml.Free;
  339. end;
  340. end
  341. else
  342. begin
  343. //rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.ToYaml);
  344. {$IFNDEF FPC}
  345. //avoid return unicode escaped chars if string
  346. if rField.FieldType.TypeKind in [tkString, tkLString, tkWString, tkUString] then
  347. {$IFDEF DELPHIRX103_UP}
  348. rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.Value.AsString)
  349. {$ELSE}
  350. rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.YamlString.ToString)
  351. {$ENDIF}
  352. else rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.Value.AsString);
  353. {$ELSE}
  354. rValue := DeserializeType(aObject,rField.FieldType.TypeKind,aName,member.Value.AsString);
  355. {$ENDIF}
  356. end;
  357. end;
  358. if not rValue.IsEmpty then rField.SetValue(aRecord.GetReferenceToRawData,rValue);
  359. end;
  360. Result := aRecord;
  361. finally
  362. ctx.Free;
  363. end;
  364. end;
  365. {$ENDIF}
  366. constructor TRTTIYaml.Create(aSerializeLevel : TSerializeLevel; aUseEnumNames : Boolean = True);
  367. begin
  368. fSerializeLevel := aSerializeLevel;
  369. fUseEnumNames := aUseEnumNames;
  370. fUseYamlCaseSense := False;
  371. end;
  372. function TRTTIYaml.DeserializeClass(aType: TClass; const aYaml: TYamlObject): TObject;
  373. begin
  374. Result := nil;
  375. if (aYaml = nil) or ((aYaml as TYamlValue) is TYamlNull) or (aYaml.Count = 0) then Exit;
  376. try
  377. Result := aType.Create;
  378. Result := DeserializeObject(Result,aYaml);
  379. except
  380. on E : Exception do
  381. begin
  382. Result.Free;
  383. raise EYamlDeserializeError.CreateFmt('Deserialize error class "%s" : %s',[aType.ClassName,e.Message]);
  384. end;
  385. end;
  386. end;
  387. function TRTTIYaml.DeserializeObject(aObject: TObject; const aYaml: TYamlObject): TObject;
  388. var
  389. ctx: TRttiContext;
  390. rType: TRttiType;
  391. rProp: TRttiProperty;
  392. {$IFNDEF FPC}
  393. attr: TCustomAttribute;
  394. {$ENDIF}
  395. propertyname : string;
  396. begin
  397. Result := aObject;
  398. if (aYaml = nil) or ((aYaml as TYamlValue) is TYamlNull) or (aYaml.Count = 0) or (Result = nil) then Exit;
  399. try
  400. rType := ctx.GetType(aObject.ClassInfo);
  401. try
  402. for rProp in rType.GetProperties do
  403. begin
  404. {$IFNDEF FPC}
  405. if ((fSerializeLevel = slPublicProperty) and (rProp.PropertyType.IsPublicType))
  406. or ((fSerializeLevel = slPublishedProperty) and ((IsPublishedProp(aObject,rProp.Name)) or (rProp.Name = 'List'))) then
  407. {$ENDIF}
  408. begin
  409. if ((rProp.IsWritable) or (rProp.Name = 'List')) and (IsAllowedProperty(aObject,rProp.Name)) then
  410. begin
  411. propertyname := rProp.Name;
  412. {$IFNDEF FPC}
  413. for attr in rProp.GetAttributes do if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
  414. if rProp.Name = 'List' then
  415. begin
  416. Result := DeserializeList(Result,propertyname,aYaml);
  417. end
  418. else if (rProp.GetValue(aObject).IsObject) and (IsGenericList(rProp.GetValue(aObject).AsObject)) then
  419. begin
  420. DeserializeList(rProp.GetValue(aObject).AsObject,'List',TYamlObject(aYaml.GetValue(propertyname)));
  421. end
  422. else if (not rProp.GetValue(aObject).IsObject) and (IsGenericXArray(rProp.GetValue(aObject){$IFNDEF NEXTGEN}.TypeInfo.Name{$ELSE}.TypeInfo.NameFld.ToString{$ENDIF})) then
  423. begin
  424. DeserializeXArray(Result,rProp.GetValue(aObject),rProp,propertyname,aYaml);
  425. end
  426. else
  427. {$ENDIF}
  428. Result := DeserializeProperty(Result,propertyname,rProp,aYaml);
  429. end;
  430. end;
  431. end;
  432. finally
  433. ctx.Free;
  434. end;
  435. except
  436. on E : Exception do
  437. begin
  438. Result.Free;
  439. raise EYamlDeserializeError.CreateFmt('Deserialize error for object "%s" : %s',[aObject.ClassName,e.Message]);
  440. end;
  441. end;
  442. end;
  443. {$IFNDEF FPC}
  444. function TRTTIYaml.DeserializeList(aObject: TObject; const aName : string; const aYaml: TYamlObject) : TObject;
  445. var
  446. ctx : TRttiContext;
  447. rType : TRttiType;
  448. yArray : TYamlArray;
  449. member : TYamlPair;
  450. rvalue : TValue;
  451. i : Integer;
  452. rProp : TRttiProperty;
  453. {$IFNDEF DELPHIRX103_UP}
  454. rfield : TRttiField;
  455. {$ENDIF}
  456. begin
  457. Result := aObject;
  458. rType := ctx.GetType(aObject.ClassInfo);
  459. try
  460. rProp := rType.GetProperty('List');
  461. if rProp = nil then Exit;
  462. finally
  463. ctx.Free;
  464. end;
  465. member := GetYamlPairByName(aYaml,aName);
  466. //var a := aYaml.ToYaml;
  467. if member = nil then yArray := TYamlArray(aYaml) //TYamlObject.ParseYamlValue(aYaml.ToYaml) as TYamlArray
  468. else yArray := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlArray;
  469. rvalue := DeserializeDynArray(rProp.PropertyType.Handle,Result,yArray);
  470. if not rValue.IsEmpty then
  471. begin
  472. {$IFDEF DELPHIRX103_UP}
  473. if (TObjectList<TObject>(aObject) <> nil) and (rvalue.IsArray) then
  474. begin
  475. TObjectList<TObject>(aObject).Clear;
  476. for i := 0 to rvalue.GetArrayLength - 1 do
  477. begin
  478. TObjectList<TObject>(aObject).Add(rvalue.GetArrayElement(i).AsObject);
  479. end;
  480. end;
  481. {$ELSE}
  482. for rfield in rType.GetFields do
  483. begin
  484. if rfield.Name = 'FOwnsObjects' then rfield.SetValue(aObject,True);
  485. //if rfield.Name = 'FCount' then rfield.SetValue(aObject,i);
  486. if rfield.Name = 'FItems' then
  487. begin
  488. //if TList(aObject) <> nil then TList(aObject).Clear;
  489. //rfield.GetValue(aObject).AsObject.Free;// aValue.GetReferenceToRawData)
  490. rfield.SetValue(aObject,rValue);// .SetDynArrayProp(aObject,'fItems',Result);
  491. Break;
  492. end;
  493. end;
  494. rProp := rType.GetProperty('Count');
  495. rProp.SetValue(aObject,i);
  496. {$ENDIF}
  497. end;
  498. end;
  499. {$ENDIF}
  500. {$IFNDEF FPC}
  501. procedure TRTTIYaml.DeserializeXArray(Instance : TObject; aRecord : TValue; aProperty : TRttiProperty; const aPropertyName : string; aYaml : TYamlObject);
  502. var
  503. ctx : TRttiContext;
  504. rRec : TRttiRecordType;
  505. rfield : TRttiField;
  506. rValue : TValue;
  507. member : TYamlPair;
  508. yArray : TYamlArray;
  509. begin
  510. rRec := ctx.GetType(aRecord.TypeInfo).AsRecord;
  511. try
  512. rfield := rRec.GetField('fArray');
  513. if rfield <> nil then
  514. begin
  515. rValue := nil;
  516. //member := TYamlPair(aYaml.GetValue(rField.Name));
  517. member := GetYamlPairByName(aYaml,aPropertyName);
  518. if (member <> nil) and (rField.FieldType.TypeKind = tkDynArray) then
  519. begin
  520. yArray := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlArray;
  521. try
  522. rValue := DeserializeDynArray(rField.FieldType.Handle,nil,yArray);
  523. finally
  524. yArray.Free;
  525. end;
  526. end;
  527. end;
  528. if not rValue.IsEmpty then rField.SetValue(aRecord.GetReferenceToRawData,rValue);
  529. aProperty.SetValue(Instance,aRecord);
  530. finally
  531. ctx.Free;
  532. end;
  533. end;
  534. {$ENDIF}
  535. function TRTTIYaml.DeserializeProperty(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aYaml : TYamlObject) : TObject;
  536. var
  537. rValue : TValue;
  538. member : TYamlPair;
  539. objClass: TClass;
  540. yArray : TYamlArray;
  541. Yaml : TYamlObject;
  542. begin
  543. Result := aObject;
  544. rValue := nil;
  545. //member := TYamlPair(aYaml.GetValue(aName));
  546. member := GetYamlPairByName(aYaml,aName);
  547. if member <> nil then
  548. begin
  549. case aProperty.PropertyType.TypeKind of
  550. tkDynArray :
  551. begin
  552. yArray := member.Value as TYamlArray;
  553. {$IFNDEF FPC}
  554. aProperty.SetValue(aObject,DeserializeDynArray(aProperty.PropertyType.Handle,Result,yArray));
  555. {$ELSE}
  556. DeserializeDynArray(aProperty.PropertyType.Handle,aName,Result,yArray);
  557. {$ENDIF}
  558. Exit;
  559. end;
  560. tkClass :
  561. begin
  562. //if (member.YamlValue is TYamlObject) then
  563. begin
  564. Yaml := TYamlObject(TYamlObject.ParseYamlValue(member.ToYaml));
  565. try
  566. if aProperty.GetValue(aObject).AsObject = nil then
  567. begin
  568. {$IFNDEF FPC}
  569. objClass := aProperty.PropertyType.Handle^.TypeData.ClassType;
  570. rValue := DeserializeClass(objClass,Yaml);
  571. {$ELSE}
  572. objClass := GetObjectPropClass(aObject,aName);
  573. //objClass := GetTypeData(aProperty.PropertyType.Handle)^.ClassType;
  574. rValue := DeserializeClass(objClass,Yaml);
  575. SetObjectProp(aObject,aName,rValue.AsObject);
  576. Exit;
  577. {$ENDIF}
  578. end
  579. else
  580. begin
  581. rValue := DeserializeObject(aProperty.GetValue(aObject).AsObject,Yaml);
  582. Exit;
  583. end;
  584. finally
  585. Yaml.Free;
  586. end;
  587. end
  588. end;
  589. {$IFNDEF FPC}
  590. tkRecord :
  591. begin
  592. Yaml := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlObject;
  593. try
  594. rValue := DeserializeRecord(aProperty.GetValue(aObject),aObject,Yaml);
  595. finally
  596. Yaml.Free;
  597. end;
  598. end;
  599. {$ENDIF}
  600. else
  601. begin
  602. {$IFNDEF FPC}
  603. //avoid return unicode escaped chars if string
  604. if aProperty.PropertyType.TypeKind in [tkString, tkLString, tkWString, tkUString] then
  605. {$IFDEF DELPHIRX103_UP}
  606. rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.Value.AsString)
  607. {$ELSE}
  608. rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.YamlString.ToString)
  609. {$ENDIF}
  610. else rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.Value.AsString);
  611. {$ELSE}
  612. rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aName,member.Value.AsString);
  613. if not rValue.IsEmpty then SetPropertyValue(aObject,aName,rValue);
  614. {$ENDIF}
  615. end;
  616. end;
  617. {$IFNDEF FPC}
  618. if not rValue.IsEmpty then aProperty.SetValue(Result,rValue);
  619. {$ENDIF}
  620. end;
  621. end;
  622. {$IFNDEF FPC}
  623. function TRTTIYaml.DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
  624. var
  625. i : Integer;
  626. value : string;
  627. fsettings : TFormatSettings;
  628. begin
  629. try
  630. value := AnsiDequotedStr(aValue,'"');
  631. case aType of
  632. tkString, tkLString, tkWString, tkUString :
  633. begin
  634. Result := value;
  635. end;
  636. tkChar, tkWChar :
  637. begin
  638. Result := value;
  639. end;
  640. tkInteger :
  641. begin
  642. Result := StrToInt(value);
  643. end;
  644. tkInt64 :
  645. begin
  646. Result := StrToInt64(value);
  647. end;
  648. tkFloat :
  649. begin
  650. if aTypeInfo = TypeInfo(TDateTime) then
  651. begin
  652. if value <> 'null' then Result := JsonDateToDateTime(value);
  653. end
  654. else if aTypeInfo = TypeInfo(TDate) then
  655. begin
  656. Result := StrToDate(value);
  657. end
  658. else if aTypeInfo = TypeInfo(TTime) then
  659. begin
  660. Result := StrToTime(value);
  661. end
  662. else
  663. begin
  664. fsettings := TFormatSettings.Create;
  665. Result := StrToFloat(StringReplace(value,'.',fsettings.DecimalSeparator,[]));
  666. end;
  667. end;
  668. tkEnumeration :
  669. begin
  670. if aTypeInfo = System.TypeInfo(Boolean) then
  671. begin
  672. Result := StrToBool(value);
  673. end
  674. else
  675. begin
  676. //if fUseEnumNames then TValue.Make(GetEnumValue(aTypeInfo,value),aTypeInfo, Result)
  677. // else TValue.Make(StrToInt(value),aTypeInfo, Result);
  678. if not TryStrToInt(value,i) then TValue.Make(GetEnumValue(aTypeInfo,value),aTypeInfo, Result)
  679. else TValue.Make(StrToInt(value),aTypeInfo, Result);
  680. end;
  681. end;
  682. tkSet :
  683. begin
  684. i := StringToSet(aTypeInfo,value);
  685. TValue.Make(@i,aTypeInfo,Result);
  686. end;
  687. else
  688. begin
  689. //raise EclYamlSerializerError.Create('Not supported data type!');
  690. end;
  691. end;
  692. except
  693. on E : Exception do
  694. begin
  695. raise EYamlDeserializeError.CreateFmt('Deserialize error type "%s.%s" : %s',[aObject.ClassName,GetTypeName(aTypeInfo),e.Message]);
  696. end;
  697. end;
  698. end;
  699. {$ELSE}
  700. function TRTTIYaml.DeserializeType(aObject : TObject; aType : TTypeKind; const aPropertyName, aValue: string) : TValue;
  701. var
  702. value : string;
  703. propinfo : PPropInfo;
  704. fsettings : TFormatSettings;
  705. begin
  706. try
  707. value := AnsiDequotedStr(aValue,'"');
  708. if value = '' then
  709. begin
  710. Result := nil;
  711. Exit;
  712. end;
  713. propinfo := GetPropInfo(aObject,aPropertyName);
  714. //case propinfo.PropType.Kind of
  715. case aType of
  716. tkString, tkLString, tkWString, tkUString, tkAString :
  717. begin
  718. Result := value;
  719. //SetStrProp(aObject,propinfo,value);
  720. end;
  721. tkChar, tkWChar :
  722. begin
  723. Result := value;
  724. end;
  725. tkInteger :
  726. begin
  727. Result := StrToInt(value);
  728. end;
  729. tkInt64 :
  730. begin
  731. Result := StrToInt64(value);
  732. end;
  733. tkFloat :
  734. begin
  735. if propinfo.PropType = TypeInfo(TDateTime) then
  736. begin
  737. Result := JsonDateToDateTime(value);
  738. end
  739. else if propinfo.PropType = TypeInfo(TDate) then
  740. begin
  741. Result := StrToDate(value);
  742. end
  743. else if propinfo.PropType = TypeInfo(TTime) then
  744. begin
  745. Result := StrToTime(value);
  746. end
  747. else
  748. begin
  749. fsettings := DefaultFormatSettings;
  750. Result := StrToFloat(StringReplace(value,'.',fsettings.DecimalSeparator,[]));
  751. end;
  752. end;
  753. tkEnumeration:
  754. begin
  755. Result := value;
  756. end;
  757. tkBool :
  758. begin
  759. Result := StrToBool(value);
  760. end;
  761. tkSet :
  762. begin
  763. Result := value;
  764. end;
  765. else
  766. begin
  767. //raise EclYamlSerializerError.Create('Not supported data type!');
  768. end;
  769. end;
  770. //if not Result.IsEmpty then SetPropertyValue(aObject,propinfo,Result);
  771. except
  772. on E : Exception do
  773. begin
  774. raise EYamlDeserializeError.CreateFmt('Deserialize error type "%s" : %s',[aObject.ClassName,e.Message]);
  775. end;
  776. end;
  777. end;
  778. {$ENDIF}
  779. function TRTTIYaml.IsAllowedProperty(aObject : TObject; const aPropertyName : string) : Boolean;
  780. var
  781. propname : string;
  782. begin
  783. Result := True;
  784. propname := aPropertyName.ToLower;
  785. if IsGenericList(aObject) then
  786. begin
  787. if (propname = 'capacity') or (propname = 'count') or (propname = 'ownsobjects') then Result := False;
  788. end
  789. else if (propname = 'refcount') then Result := False;
  790. end;
  791. function TRTTIYaml.IsGenericList(aObject : TObject) : Boolean;
  792. var
  793. cname : string;
  794. begin
  795. if aObject = nil then Exit(False);
  796. cname := aObject.ClassName;
  797. Result := (cname.StartsWith('TObjectList')) or (cname.StartsWith('TList'));
  798. end;
  799. function TRTTIYaml.IsGenericXArray(const aClassName : string) : Boolean;
  800. begin
  801. Result := aClassName.StartsWith('TXArray');
  802. end;
  803. function TRTTIYaml.GetYamlPairByName(aYaml: TYamlObject; const aName: string): TYamlPair;
  804. var
  805. candidate : TYamlPair;
  806. yvalue : TYamlValue;
  807. i : Integer;
  808. begin
  809. if fUseYamlCaseSense then
  810. begin
  811. yvalue := aYaml.GetValue(aName);
  812. if yvalue <> nil then Result := TYamlPair(yvalue);
  813. Exit;
  814. end
  815. else
  816. begin
  817. if aYaml <> nil then
  818. for i := 0 to aYaml.Count - 1 do
  819. begin
  820. candidate := aYaml.Pairs[I];
  821. if (candidate = nil) or (candidate.Value = nil) then Exit(nil);
  822. if CompareText(candidate.Name,aName) = 0 then
  823. Exit(candidate);
  824. end;
  825. end;
  826. Result := nil;
  827. end;
  828. function TRTTIYaml.GetPropertyValue(Instance : TObject; const PropertyName : string) : TValue;
  829. var
  830. pinfo : PPropInfo;
  831. begin
  832. Result := nil;
  833. pinfo := GetPropInfo(Instance,PropertyName);
  834. case pinfo.PropType^.Kind of
  835. tkInteger : Result := GetOrdProp(Instance,pinfo);
  836. tkInt64 : Result := GetInt64Prop(Instance,PropertyName);
  837. tkFloat : Result := GetFloatProp(Instance,PropertyName);
  838. tkChar : Result := Char(GetOrdProp(Instance,PropertyName));
  839. {$IFDEF FPC}
  840. tkWString : Result := GetWideStrProp(Instance,PropertyName);
  841. tkSString,
  842. tkAString,
  843. {$ELSE}
  844. tkWString,
  845. {$ENDIF}
  846. tkLString : Result := GetStrProp(Instance,pinfo);
  847. {$IFDEF FPC}
  848. tkEnumeration :
  849. begin
  850. if fUseEnumNames then Result := GetEnumName(pinfo.PropType,GetOrdProp(Instance,PropertyName))
  851. else Result := GetOrdProp(Instance,PropertyName);
  852. end;
  853. {$ELSE}
  854. tkEnumeration :
  855. begin
  856. if fUseEnumNames then Result := GetEnumName(@pinfo.PropType,GetOrdProp(Instance,PropertyName))
  857. else Result := GetOrdProp(Instance,PropertyName);
  858. end;
  859. {$ENDIF}
  860. tkSet : Result := GetSetProp(Instance,pinfo,True);
  861. {$IFNDEF FPC}
  862. tkClass :
  863. {$ELSE}
  864. tkBool : Result := Boolean(GetOrdProp(Instance,pinfo));
  865. tkObject :
  866. {$ENDIF} Result := GetObjectProp(Instance,pinfo);
  867. tkDynArray : Result := GetDynArrayProp(Instance,pinfo);
  868. end;
  869. end;
  870. function TRTTIYaml.GetPropertyValueFromObject(Instance : TObject; const PropertyName : string) : TValue;
  871. var
  872. ctx : TRttiContext;
  873. rprop : TRttiProperty;
  874. begin
  875. rprop := ctx.GetType(Instance.ClassInfo).GetProperty(PropertyName);
  876. Result := rprop.GetValue(Instance);
  877. end;
  878. {$IFNDEF FPC}
  879. function TRTTIYaml.GetFieldValueFromRecord(aValue : TValue; const FieldName : string) : TValue;
  880. var
  881. ctx : TRttiContext;
  882. rec : TRttiRecordType;
  883. rfield : TRttiField;
  884. begin
  885. rec := ctx.GetType(aValue.TypeInfo).AsRecord;
  886. rfield := rec.GetField(FieldName);
  887. if rfield <> nil then Result := rField.GetValue(aValue.GetReferenceToRawData)
  888. else Result := nil;
  889. end;
  890. {$ENDIF}
  891. procedure TRTTIYaml.SetPropertyValue(Instance : TObject; const PropertyName : string; aValue : TValue);
  892. var
  893. pinfo : PPropInfo;
  894. begin
  895. pinfo := GetPropInfo(Instance,PropertyName);
  896. SetPropertyValue(Instance,pinfo,aValue);
  897. end;
  898. procedure TRTTIYaml.SetPropertyValue(Instance : TObject; aPropInfo : PPropInfo; aValue : TValue);
  899. begin
  900. case aPropInfo.PropType^.Kind of
  901. tkInteger : SetOrdProp(Instance,aPropInfo,aValue.AsInteger);
  902. tkInt64 : SetInt64Prop(Instance,aPropInfo,aValue.AsInt64);
  903. tkFloat : SetFloatProp(Instance,aPropInfo,aValue.AsExtended);
  904. tkChar : SetOrdProp(Instance,aPropInfo,aValue.AsOrdinal);
  905. {$IFDEF FPC}
  906. tkWString : SetWideStrProp(Instance,aPropInfo,aValue.AsString);
  907. tkSString,
  908. tkAString,
  909. {$ELSE}
  910. tkWString,
  911. {$ENDIF}
  912. tkLString : SetStrProp(Instance,aPropInfo,aValue.AsString);
  913. {$IFDEF FPC}
  914. tkBool : SetOrdProp(Instance,aPropInfo,aValue.AsOrdinal);
  915. tkSet : LoadSetProperty(Instance,aPropInfo,aValue.AsString);
  916. {$ENDIF}
  917. tkEnumeration : SetEnumProp(Instance,aPropInfo,aValue.AsString);
  918. {$IFNDEF FPC}
  919. tkClass :
  920. {$ELSE}
  921. tkObject :
  922. {$ENDIF} SetObjectProp(Instance,aPropInfo,aValue.AsObject);
  923. end;
  924. end;
  925. {$IFDEF FPC}
  926. procedure TRTTIYaml.LoadSetProperty(aInstance : TObject; aPropInfo: PPropInfo; const aValue: string);
  927. type
  928. TCardinalSet = set of 0..SizeOf(Cardinal) * 8 - 1;
  929. const
  930. Delims = [' ', ',', '[', ']'];
  931. var
  932. TypeInfo: PTypeInfo;
  933. W: Cardinal;
  934. I, N: Integer;
  935. Count: Integer;
  936. EnumName: string;
  937. begin
  938. W := 0;
  939. TypeInfo := GetTypeData(GetPropType(aPropInfo))^.CompType;
  940. Count := WordCount(aValue, Delims);
  941. for N := 1 to Count do
  942. begin
  943. EnumName := ExtractWord(N, aValue, Delims);
  944. try
  945. I := GetEnumValue(TypeInfo, EnumName);
  946. if I >= 0 then Include(TCardinalSet(W),I);
  947. except
  948. end;
  949. end;
  950. SetOrdProp(aInstance,aPropInfo,W);
  951. end;
  952. {$ENDIF}
  953. function TRTTIYaml.Serialize(aObject: TObject): TYamlObject;
  954. var
  955. ctx: TRttiContext;
  956. {$IFNDEF FPC}
  957. attr : TCustomAttribute;
  958. comment : string;
  959. {$ENDIF}
  960. rType: TRttiType;
  961. rProp: TRttiProperty;
  962. ypair : TYamlPair;
  963. ExcludeSerialize : Boolean;
  964. propertyname : string;
  965. begin
  966. if (aObject = nil) then
  967. begin
  968. Result := nil;
  969. Exit;
  970. end;
  971. Result := TYamlObject.Create;
  972. try
  973. rType := ctx.GetType(aObject.ClassInfo);
  974. try
  975. //s := rType.ToString;
  976. for rProp in rType.GetProperties do
  977. begin
  978. ExcludeSerialize := False;
  979. propertyname := rProp.Name;
  980. {$IFNDEF FPC}
  981. comment := '';
  982. for attr in rProp.GetAttributes do
  983. begin
  984. if attr is TNotSerializableProperty then ExcludeSerialize := True
  985. else if attr is TCommentProperty then comment := TCommentProperty(attr).Comment
  986. else if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
  987. end;
  988. if ((fSerializeLevel = slPublicProperty) and (rProp.PropertyType.IsPublicType))
  989. or ((fSerializeLevel = slPublishedProperty) and ((IsPublishedProp(aObject,rProp.Name)) or (rProp.Name = 'List'))) then
  990. {$ENDIF}
  991. begin
  992. if (IsAllowedProperty(aObject,propertyname)) and (not ExcludeSerialize) then
  993. begin
  994. //add comment as pair
  995. {$IFNDEF FPC}
  996. if comment <> '' then Result.AddPair(TYamlPair.Create('#',TYamlComment.Create(Comment)));
  997. {$ENDIF}
  998. begin
  999. if (rProp.GetValue(aObject).IsObject) and (IsGenericList(rProp.GetValue(aObject).AsObject)) then
  1000. begin
  1001. ypair := Serialize(propertyname,GetPropertyValueFromObject(rProp.GetValue(aObject).AsObject,'List'));
  1002. end
  1003. {$IFNDEF FPC}
  1004. else if (not rProp.GetValue(aObject).IsObject) and (IsGenericXArray(rProp.GetValue(aObject){$IFNDEF NEXTGEN}.TypeInfo.Name{$ELSE}.TypeInfo.NameFld.ToString{$ENDIF})) then
  1005. begin
  1006. ypair := Serialize(propertyname,GetFieldValueFromRecord(rProp.GetValue(aObject),'fArray'));
  1007. end
  1008. {$ENDIF}
  1009. else
  1010. begin
  1011. {$IFNDEF FPC}
  1012. ypair := Serialize(propertyname,rProp.GetValue(aObject));
  1013. {$ELSE}
  1014. ypair := Serialize(aObject,rProp.PropertyType.TypeKind,propertyname);
  1015. {$ENDIF}
  1016. end;
  1017. //s := jpair.YamlValue.ToString;
  1018. if ypair <> nil then
  1019. begin
  1020. Result.AddPair(ypair);
  1021. end
  1022. else ypair.Free;
  1023. end;
  1024. //Result.AddPair(Serialize(rProp.Name,rProp.GetValue(aObject)));
  1025. //s := Result.ToYaml;
  1026. end;
  1027. end;
  1028. end;
  1029. finally
  1030. ctx.Free;
  1031. end;
  1032. except
  1033. on E : Exception do
  1034. begin
  1035. Result.Free;
  1036. raise EYamlSerializeError.CreateFmt('Serialize error object "%s" : %s',[aObject.ClassName,e.Message]);
  1037. end;
  1038. end;
  1039. end;
  1040. function TRTTIYaml.GetValue(aAddr: Pointer; aType: TRTTIType): TValue;
  1041. begin
  1042. TValue.Make(aAddr,aType.Handle,Result);
  1043. end;
  1044. function TRTTIYaml.GetValue(aAddr: Pointer; aTypeInfo: PTypeInfo): TValue;
  1045. begin
  1046. TValue.Make(aAddr,aTypeInfo,Result);
  1047. end;
  1048. {$IFNDEF FPC}
  1049. function TRTTIYaml.Serialize(const aName : string; aValue : TValue) : TYamlPair;
  1050. var
  1051. ctx: TRttiContext;
  1052. rRec : TRttiRecordType;
  1053. rField : TRttiField;
  1054. rDynArray : TRTTIDynamicArrayType;
  1055. Yaml : TYamlObject;
  1056. yArray : TYamlArray;
  1057. ypair : TYamlPair;
  1058. yvalue : TYamlValue;
  1059. i : Integer;
  1060. begin
  1061. Result := TYamlPair.Create(aName,nil);
  1062. //Result.YamlString := TYamlString(aName);
  1063. try
  1064. case avalue.Kind of
  1065. tkDynArray :
  1066. begin
  1067. yArray := TYamlArray.Create;
  1068. rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
  1069. try
  1070. for i := 0 to aValue.GetArrayLength - 1 do
  1071. begin
  1072. if not GetValue(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType).IsEmpty then
  1073. begin
  1074. yvalue := nil;
  1075. ypair := Serialize(aName,GetValue(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType));
  1076. try
  1077. //jValue := TYamlValue(jPair.YamlValue.Clone);
  1078. yvalue := ypair.Value;
  1079. if yvalue <> nil then
  1080. begin
  1081. yArray.AddElement(yvalue);
  1082. ypair.Value.Owned := False;
  1083. end;
  1084. finally
  1085. ypair.Free;
  1086. if yvalue <> nil then yvalue.Owned := True;
  1087. end;
  1088. end;
  1089. end;
  1090. Result.Value := yArray;
  1091. finally
  1092. ctx.Free;
  1093. end;
  1094. end;
  1095. tkClass :
  1096. begin
  1097. Result.Value := TYamlValue(Serialize(aValue.AsObject));
  1098. end;
  1099. tkString, tkLString, tkWString, tkUString :
  1100. begin
  1101. Result.Value := TYamlString.Create(aValue.AsString);
  1102. end;
  1103. tkChar, tkWChar :
  1104. begin
  1105. Result.Value := TYamlString.Create(aValue.AsString);
  1106. end;
  1107. tkInteger :
  1108. begin
  1109. Result.Value := TYamlInteger.Create(aValue.AsInteger);
  1110. end;
  1111. tkInt64 :
  1112. begin
  1113. Result.Value := TYamlInteger.Create(aValue.AsInt64);
  1114. end;
  1115. tkFloat :
  1116. begin
  1117. if aValue.TypeInfo = TypeInfo(TDateTime) then
  1118. begin
  1119. Result.Value := TYamlString.Create(DateTimeToJsonDate(aValue.AsExtended));
  1120. end
  1121. else if aValue.TypeInfo = TypeInfo(TDate) then
  1122. begin
  1123. Result.Value := TYamlString.Create(DateToStr(aValue.AsExtended));
  1124. end
  1125. else if aValue.TypeInfo = TypeInfo(TTime) then
  1126. begin
  1127. Result.Value := TYamlString.Create(TimeToStr(aValue.AsExtended));
  1128. end
  1129. else
  1130. begin
  1131. Result.Value := TYamlFloat.Create(aValue.AsExtended);
  1132. end;
  1133. end;
  1134. tkEnumeration :
  1135. begin
  1136. if (aValue.TypeInfo = System.TypeInfo(Boolean)) then
  1137. begin
  1138. Result.Value := TYamlBoolean.Create(aValue.AsBoolean);
  1139. end
  1140. else
  1141. begin
  1142. //Result.YamlValue := TYamlString.Create(GetEnumName(aValue.TypeInfo,aValue.AsOrdinal));
  1143. if fUseEnumNames then Result.Value := TYamlString.Create(aValue.ToString)
  1144. else Result.Value := TYamlInteger.Create(GetEnumValue(aValue.TypeInfo,aValue.ToString));
  1145. end;
  1146. end;
  1147. tkSet :
  1148. begin
  1149. Result.Value := TYamlString.Create(aValue.ToString);
  1150. end;
  1151. tkRecord :
  1152. begin
  1153. rRec := ctx.GetType(aValue.TypeInfo).AsRecord;
  1154. try
  1155. Yaml := TYamlObject.Create;
  1156. for rField in rRec.GetFields do
  1157. begin
  1158. Yaml.AddPair(Serialize(rField.name,rField.GetValue(aValue.GetReferenceToRawData)));
  1159. end;
  1160. Result.Value := Yaml;
  1161. finally
  1162. ctx.Free;
  1163. end;
  1164. end;
  1165. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  1166. begin
  1167. //skip these properties
  1168. //FreeAndNil(Result);
  1169. end
  1170. else
  1171. begin
  1172. raise EYamlSerializeError.CreateFmt(cNotSupportedDataType,[aName,GetTypeName(aValue.TypeInfo)]);
  1173. end;
  1174. end;
  1175. if Result.Value = nil then Result.Value := TYamlNull.Create;
  1176. except
  1177. on E : Exception do
  1178. begin
  1179. Result.Free;
  1180. raise EYamlSerializeError.CreateFmt('Serialize error class "%s.%s" : %s',[aName,aValue.ToString,e.Message]);
  1181. end;
  1182. end;
  1183. end;
  1184. {$ELSE}
  1185. function TRTTIYaml.GetPropType(aPropInfo: PPropInfo): PTypeInfo;
  1186. begin
  1187. Result := aPropInfo^.PropType;
  1188. end;
  1189. function TRTTIYaml.FloatProperty(aObject : TObject; aPropInfo: PPropInfo): string;
  1190. const
  1191. Precisions: array[TFloatType] of Integer = (7, 15, 18, 18, 19);
  1192. var
  1193. fsettings : TFormatSettings;
  1194. begin
  1195. fsettings := FormatSettings;
  1196. Result := StringReplace(FloatToStrF(GetFloatProp(aObject, aPropInfo), ffGeneral,
  1197. Precisions[GetTypeData(GetPropType(aPropInfo))^.FloatType],0),
  1198. '.',fsettings.DecimalSeparator,[rfReplaceAll]);
  1199. end;
  1200. function TRTTIYaml.Serialize(const aName : string; aValue : TValue) : TYamlPair;
  1201. begin
  1202. Result := TYamlPair.Create(aName,nil);
  1203. //Result.YamlString := TYamlString(aName);
  1204. try
  1205. case avalue.Kind of
  1206. tkClass :
  1207. begin
  1208. Result.Value := TYamlValue(Serialize(aValue.AsObject));
  1209. end;
  1210. tkString, tkLString, tkWString, tkUString :
  1211. begin
  1212. Result.Value := TYamlString.Create(aValue.AsString);
  1213. end;
  1214. tkChar, tkWChar :
  1215. begin
  1216. Result.Value := TYamlString.Create(aValue.AsString);
  1217. end;
  1218. tkInteger :
  1219. begin
  1220. Result.Value := TYamlInteger.Create(aValue.AsInteger);
  1221. end;
  1222. tkInt64 :
  1223. begin
  1224. Result.Value := TYamlInteger.Create(aValue.AsInt64);
  1225. end;
  1226. tkFloat :
  1227. begin
  1228. if aValue.TypeInfo = TypeInfo(TDateTime) then
  1229. begin
  1230. Result.Value := TYamlString.Create(DateTimeToJsonDate(aValue.AsExtended));
  1231. end
  1232. else if aValue.TypeInfo = TypeInfo(TDate) then
  1233. begin
  1234. Result.Value := TYamlString.Create(DateToStr(aValue.AsExtended));
  1235. end
  1236. else if aValue.TypeInfo = TypeInfo(TTime) then
  1237. begin
  1238. Result.Value := TYamlString.Create(TimeToStr(aValue.AsExtended));
  1239. end
  1240. else
  1241. begin
  1242. Result.Value := TYamlFloat.Create(aValue.AsExtended);
  1243. end;
  1244. end;
  1245. tkEnumeration :
  1246. begin
  1247. if (aValue.TypeInfo = System.TypeInfo(Boolean)) then
  1248. begin
  1249. Result.Value := TYamlBoolean.Create(aValue.AsBoolean);
  1250. end
  1251. else
  1252. begin
  1253. //Result.YamlValue := TYamlString.Create(GetEnumName(aValue.TypeInfo,aValue.AsOrdinal));
  1254. if fUseEnumNames then Result.Value := TYamlString.Create(aValue.ToString)
  1255. else Result.Value := TYamlInteger.Create(GetEnumValue(aValue.TypeInfo,aValue.ToString));
  1256. end;
  1257. end;
  1258. tkSet :
  1259. begin
  1260. Result.Value := TYamlString.Create(aValue.ToString);
  1261. end;
  1262. else
  1263. begin
  1264. //raise EYamlDeserializeError.CreateFmt('Not supported type "%s":%d',[aName,Integer(aValue.Kind)]);
  1265. end;
  1266. end;
  1267. if Result.Value = nil then Result.Value := TYamlNull.Create;
  1268. except
  1269. Result.Free;
  1270. end;
  1271. end;
  1272. function TRTTIYaml.Serialize(aObject : TObject; aType : TTypeKind; const aPropertyName : string) : TYamlPair;
  1273. var
  1274. propinfo : PPropInfo;
  1275. yArray : TYamlArray;
  1276. ypair : TYamlPair;
  1277. yvalue : TYamlValue;
  1278. i : Integer;
  1279. pArr : Pointer;
  1280. rValue : TValue;
  1281. rItemValue : TValue;
  1282. len : Integer;
  1283. begin
  1284. try
  1285. Result := TYamlPair.Create(aPropertyName,nil);
  1286. propinfo := GetPropInfo(aObject,aPropertyName);
  1287. //case propinfo.PropType.Kind of
  1288. case aType of
  1289. tkDynArray :
  1290. begin
  1291. len := 0;
  1292. yArray := TYamlArray.Create;
  1293. try
  1294. pArr := GetDynArrayProp(aObject,aPropertyName);
  1295. TValue.Make(@pArr,propinfo.PropType, rValue);
  1296. if rValue.IsArray then
  1297. begin
  1298. len := rValue.GetArrayLength;
  1299. for i := 0 to len - 1 do
  1300. begin
  1301. rItemValue := rValue.GetArrayElement(i);
  1302. ypair := Serialize(aPropertyName,rItemValue);
  1303. try
  1304. //jValue := TYamlValue(jPair.YamlValue.Clone);
  1305. yvalue := ypair.Value;
  1306. yArray.AddElement(yvalue);
  1307. //jPair.YamlValue.Owned := False;
  1308. finally
  1309. ypair.Free;
  1310. //jValue.Owned := True;
  1311. end;
  1312. end;
  1313. end;
  1314. Result.Value := yArray;
  1315. finally
  1316. //DynArrayClear(pArr,propinfo.PropType);
  1317. pArr := nil;
  1318. end;
  1319. end;
  1320. tkClass :
  1321. begin
  1322. Result.Value := TYamlValue(Serialize(GetObjectProp(aObject,aPropertyName)));
  1323. end;
  1324. tkString, tkLString, tkWString, tkUString, tkAString :
  1325. begin
  1326. Result.Value := TYamlString.Create(GetStrProp(aObject,aPropertyName));
  1327. end;
  1328. tkChar, tkWChar :
  1329. begin
  1330. Result.Value := TYamlString.Create(Char(GetOrdProp(aObject,aPropertyName)));
  1331. end;
  1332. tkInteger :
  1333. begin
  1334. Result.Value := TYamlInteger.Create(GetOrdProp(aObject,aPropertyName));
  1335. end;
  1336. tkInt64 :
  1337. begin
  1338. Result.Value := TYamlInteger.Create(GetOrdProp(aObject,aPropertyName));
  1339. end;
  1340. tkFloat :
  1341. begin
  1342. if propinfo.PropType = TypeInfo(TDateTime) then
  1343. begin
  1344. Result.Value := TYamlString.Create(DateTimeToJsonDate(GetFloatProp(aObject,aPropertyName)));
  1345. end
  1346. else if propinfo.PropType = TypeInfo(TDate) then
  1347. begin
  1348. Result.Value := TYamlString.Create(DateToStr(GetFloatProp(aObject,aPropertyName)));
  1349. end
  1350. else if propinfo.PropType = TypeInfo(TTime) then
  1351. begin
  1352. Result.Value := TYamlString.Create(TimeToStr(GetFloatProp(aObject,aPropertyName)));
  1353. end
  1354. else
  1355. begin
  1356. //Result.YamlValue := TYamlFloatNumber.Create(GetFloatProp(aObject,aPropertyName));
  1357. Result.Value := TYamlFloat.Create(StrToFloat(FloatProperty(aObject,propinfo)));
  1358. end;
  1359. end;
  1360. tkEnumeration,tkBool :
  1361. begin
  1362. if (propinfo.PropType = System.TypeInfo(Boolean)) then
  1363. begin
  1364. Result.Value := TYamlBoolean.Create(Boolean(GetOrdProp(aObject,aPropertyName)));
  1365. end
  1366. else
  1367. begin
  1368. if fUseEnumNames then Result.Value := TYamlString.Create(GetEnumName(propinfo.PropType,GetOrdProp(aObject,aPropertyName)))
  1369. else Result.Value := TYamlInteger.Create(GetOrdProp(aObject,aPropertyName));
  1370. //Result.YamlValue := TYamlString.Create(aValue.ToString);
  1371. end;
  1372. end;
  1373. tkSet :
  1374. begin
  1375. Result.Value := TYamlString.Create(GetSetProp(aObject,aPropertyName));
  1376. end;
  1377. {$IFNDEF FPC}
  1378. tkRecord :
  1379. begin
  1380. rRec := ctx.GetType(aValue.TypeInfo).AsRecord;
  1381. try
  1382. Yaml := TYamlObject.Create;
  1383. for rField in rRec.GetFields do
  1384. begin
  1385. Yaml.AddPair(Serialize(rField.name,rField.GetValue(aValue.GetReferenceToRawData)));
  1386. end;
  1387. Result.YamlValue := Yaml;
  1388. finally
  1389. ctx.Free;
  1390. end;
  1391. end;
  1392. {$ENDIF}
  1393. tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
  1394. begin
  1395. //skip these properties
  1396. //FreeAndNil(Result);
  1397. end
  1398. else
  1399. begin
  1400. //raise EYamlDeserializeError.CreateFmt('Not supported type "%s":%d',[aName,Integer(aValue.Kind)]);
  1401. end;
  1402. end;
  1403. if Result.Value = nil then Result.Value := TYamlNull.Create;
  1404. except
  1405. on E : Exception do
  1406. begin
  1407. Result.Free;
  1408. {$IFNDEF FPC}
  1409. raise EYamlSerializeError.CreateFmt('Serialize error class "%s.%s" : %s',[aName,aValue.ToString,e.Message]);
  1410. {$ENDIF}
  1411. end;
  1412. end;
  1413. end;
  1414. {$ENDIF}
  1415. { TYamlSerializer}
  1416. constructor TYamlSerializer.Create(aSerializeLevel: TSerializeLevel; aUseEnumNames : Boolean = True);
  1417. begin
  1418. {$IFDEF FPC}
  1419. if aSerializeLevel = TSerializeLevel.slPublicProperty then raise EYamlSerializeError.Create('FreePascal RTTI only supports published properties');
  1420. {$ENDIF}
  1421. fSerializeLevel := aSerializeLevel;
  1422. fUseEnumNames := aUseEnumNames;
  1423. fUseYamlCaseSense := False;
  1424. fRTTIYaml := TRTTIYaml.Create(aSerializeLevel,aUseEnumNames);
  1425. fRTTIYaml.UseYamlCaseSense := fUseYamlCaseSense;
  1426. end;
  1427. function TYamlSerializer.YamlToObject(aType: TClass; const aYaml: string): TObject;
  1428. var
  1429. Yaml: TYamlObject;
  1430. begin
  1431. Yaml := TYamlObject.ParseYamlValue(aYaml) as TYamlObject;
  1432. try
  1433. fRTTIYaml.DeserializeClass(aType,Yaml);
  1434. finally
  1435. Yaml.Free;
  1436. end;
  1437. end;
  1438. destructor TYamlSerializer.Destroy;
  1439. begin
  1440. fRTTIYaml.Free;
  1441. inherited;
  1442. end;
  1443. function TYamlSerializer.YamlToObject(aObject: TObject; const aYaml: string): TObject;
  1444. var
  1445. Yaml: TYamlObject;
  1446. begin
  1447. Yaml := TYamlObject(TYamlObject.ParseYamlValue(aYaml));
  1448. try
  1449. fRTTIYaml.DeserializeObject(aObject,Yaml);
  1450. finally
  1451. Yaml.Free;
  1452. end;
  1453. end;
  1454. function TYamlSerializer.ObjectToYaml(aObject : TObject): string;
  1455. var
  1456. Yaml: TYamlObject;
  1457. begin
  1458. Yaml := fRTTIYaml.Serialize(aObject);
  1459. try
  1460. Result := Yaml.ToYaml;
  1461. finally
  1462. Yaml.Free;
  1463. end;
  1464. end;
  1465. procedure TYamlSerializer.SetUseEnumNames(const Value: Boolean);
  1466. begin
  1467. fUseEnumNames := Value;
  1468. if Assigned(fRTTIYaml) then fRTTIYaml.UseEnumNames := Value;
  1469. end;
  1470. procedure TYamlSerializer.SetUseYamlCaseSense(const Value: Boolean);
  1471. begin
  1472. fUseYamlCaseSense := Value;
  1473. if Assigned(fRTTIYaml) then fRTTIYaml.UseYamlCaseSense := Value;
  1474. end;
  1475. {$IFNDEF FPC}
  1476. { TCommentProperty }
  1477. constructor TCommentProperty.Create(const aComment: string);
  1478. begin
  1479. fComment := aComment;
  1480. end;
  1481. { TCustomNameProperty }
  1482. constructor TCustomNameProperty.Create(const aName: string);
  1483. begin
  1484. fName := aName;
  1485. end;
  1486. {$ENDIF}
  1487. end.