Quick.YAML.Serializer.pas 50 KB

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