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