Quick.YAML.Serializer.pas 54 KB

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