Quick.YAML.Serializer.pas 49 KB

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