Quick.YAML.Serializer.pas 54 KB

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