Quick.YAML.Serializer.pas 50 KB

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