Quick.YAML.Serializer.pas 53 KB

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