Quick.YAML.Serializer.pas 50 KB

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