123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638 |
- { ***************************************************************************
- Copyright (c) 2015-2021 Kike P�rez
- Unit : Quick.YAML.Serializer
- Description : YAML Serializer
- Author : Kike P�rez
- Version : 1.0
- Created : 12/04/2019
- Modified : 05/08/2021
- This file is part of QuickLib: https://github.com/exilon/QuickLib
- ***************************************************************************
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- *************************************************************************** }
- unit Quick.YAML.Serializer;
- {$i QuickLib.inc}
- interface
- uses
- Classes,
- SysUtils,
- Rtti,
- TypInfo,
- {$IFDEF FPC}
- rttiutils,
- strUtils,
- Generics.Collections,
- {$ELSE}
- {$IFDEF DELPHIRX10_UP}
- System.Generics.Collections,
- {$ENDIF}
- {$ENDIF}
- DateUtils,
- Quick.Commons,
- Quick.RTTI.Utils,
- Quick.YAML,
- Quick.Value,
- Quick.Arrays;
- type
- EYamlSerializeError = class(Exception);
- EYamlDeserializeError = class(Exception);
- {$IFNDEF FPC}
- TNotSerializableProperty = class(TCustomAttribute);
- TCommentProperty = class(TCustomAttribute)
- private
- fComment : string;
- public
- constructor Create(const aComment: string);
- property Comment : string read fComment;
- end;
- TCustomNameProperty = class(TCustomAttribute)
- private
- fName : string;
- public
- constructor Create(const aName: string);
- property Name : string read fName;
- end;
- {$ENDIF}
- IYamlSerializer = interface
- ['{CA26F7AE-F1FE-41BE-9C23-723A687F60D1}']
- function YamlToObject(aType: TClass; const aYaml: string): TObject; overload;
- function YamlToObject(aObject: TObject; const aYaml: string): TObject; overload;
- function ObjectToYaml(aObject : TObject): string;
- end;
- TSerializeLevel = (slPublicProperty, slPublishedProperty);
- TRTTIYaml = class
- private
- fSerializeLevel : TSerializeLevel;
- fUseEnumNames : Boolean;
- fUseYamlCaseSense : Boolean;
- function GetValue(aAddr: Pointer; aType: TRTTIType): TValue; overload;
- //function GetValue(aAddr: Pointer; aTypeInfo: PTypeInfo): TValue; overload;
- function IsAllowedProperty(aObject : TObject; const aPropertyName : string) : Boolean;
- function IsGenericList(aObject : TObject) : Boolean;
- function IsGenericXArray(const aClassName : string) : Boolean;
- //function GetPropertyValue(Instance : TObject; const PropertyName : string) : TValue;
- function GetPropertyValueFromObject(Instance : TObject; const PropertyName : string) : TValue;
- {$IFNDEF FPC}
- function GetFieldValueFromRecord(aValue : TValue; const FieldName : string) : TValue;
- function CreateInstance(aClass: TClass): TValue; overload;
- function CreateInstance(aType: TRttiType): TValue; overload;
- {$ENDIF}
- {$IFDEF FPC}
- procedure SetPropertyValue(Instance : TObject; aPropInfo : PPropInfo; aValue : TValue); overload;
- procedure SetPropertyValue(Instance : TObject; const PropertyName : string; aValue : TValue); overload;
- function FloatProperty(aObject : TObject; aPropInfo: PPropInfo): string;
- function GetPropType(aPropInfo: PPropInfo): PTypeInfo;
- procedure LoadSetProperty(aInstance : TObject; aPropInfo: PPropInfo; const aValue: string);
- {$ENDIF}
- public
- constructor Create(aSerializeLevel : TSerializeLevel; aUseEnumNames : Boolean = True);
- property UseEnumNames : Boolean read fUseEnumNames write fUseEnumNames;
- property UseYamlCaseSense : Boolean read fUseYamlCaseSense write fUseYamlCaseSense;
- {$IFNDEF FPC}
- function DeserializeDynArray(aTypeInfo : PTypeInfo; aObject : TObject; const aYamlArray: TYamlArray) : TValue;
- function DeserializeRecord(aRecord : TValue; aObject : TObject; const aYaml : TYamlObject) : TValue;
- {$ELSE}
- procedure DeserializeDynArray(aTypeInfo: PTypeInfo; const aPropertyName : string; aObject: TObject; const aYamlArray: TYamlArray);
- {$ENDIF}
- function DeserializeClass(aType : TClass; const aYaml : TYamlObject) : TObject;
- function DeserializeObject(aObject : TObject; const aYaml : TYamlObject) : TObject; overload;
- {$IFNDEF FPC}
- function DeserializeList(aObject: TObject; const aName : string; const aYaml: TYamlObject) : TObject;
- procedure DeserializeXArray(Instance : TObject; aRecord : TValue; aProperty : TRttiProperty; const aPropertyName : string; aYaml : TYamlObject);
- {$ENDIF}
- function DeserializeProperty(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aYaml : TYamlObject) : TObject; overload;
- {$IFNDEF FPC}
- function DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
- {$ELSE}
- function DeserializeType(aObject : TObject; aType : TTypeKind; const aPropertyName, aValue: string) : TValue;
- {$ENDIF}
- {$IFNDEF FPC}
- function Serialize(const aName : string; aValue : TValue) : TYamlPair; overload;
- {$ELSE}
- function Serialize(aObject : TObject; aType : TTypeKind; const aPropertyName : string) : TYamlPair;
- function Serialize(const aName : string; aValue : TValue) : TYamlPair;
- {$ENDIF}
- function Serialize(aObject : TObject) : TYamlObject; overload;
- function GetYamlPairByName(aYaml : TYamlObject; const aName : string) : TYamlPair;
- end;
- TYamlSerializer = class(TInterfacedObject,IYamlSerializer)
- strict private
- fSerializeLevel : TSerializeLevel;
- fUseEnumNames : Boolean;
- fUseYamlCaseSense : Boolean;
- fRTTIYaml : TRTTIYaml;
- private
- procedure SetUseEnumNames(const Value: Boolean);
- procedure SetUseYamlCaseSense(const Value: Boolean);
- procedure SetSerializeLevel(const Value: TSerializeLevel);
- public
- constructor Create(aSerializeLevel: TSerializeLevel; aUseEnumNames : Boolean = True);
- destructor Destroy; override;
- property SerializeLevel : TSerializeLevel read fSerializeLevel write SetSerializeLevel;
- property UseEnumNames : Boolean read fUseEnumNames write SetUseEnumNames;
- property UseYamlCaseSense : Boolean read fUseYamlCaseSense write SetUseYamlCaseSense;
- function YamlToObject(aType : TClass; const aYaml: string) : TObject; overload;
- function YamlToObject(aObject : TObject; const aYaml: string) : TObject; overload;
- function ObjectToYaml(aObject : TObject): string;
- end;
- PPByte = ^PByte;
- resourcestring
- cNotSupportedDataType = 'Not supported "%s" data type "%s"';
- cNotSerializable = 'Object is not serializable';
- implementation
- { TRTTIYaml }
- {$IFNDEF FPC}
- function TRTTIYaml.DeserializeDynArray(aTypeInfo: PTypeInfo; aObject: TObject; const aYamlArray: TYamlArray) : TValue;
- var
- rType: PTypeInfo;
- len: NativeInt;
- pArr: Pointer;
- rItemValue: TValue;
- i: Integer;
- objClass: TClass;
- ctx : TRttiContext;
- Yaml : TYamlObject;
- rDynArray : TRttiDynamicArrayType;
- propObj : TObject;
- begin
- if GetTypeData(aTypeInfo).DynArrElType = nil then Exit;
- if not assigned(aYamlArray) then Exit;
- len := aYamlArray.Count;
- rType := GetTypeData(aTypeInfo).DynArrElType^;
- pArr := nil;
- DynArraySetLength(pArr,aTypeInfo, 1, @len);
- try
- TValue.Make(@pArr,aTypeInfo, Result);
- rDynArray := ctx.GetType(Result.TypeInfo) as TRTTIDynamicArrayType;
- try
- for i := 0 to aYamlArray.Count - 1 do
- begin
- rItemValue := nil;
- case rType.Kind of
- tkClass :
- begin
- if aYamlArray.Items[i] = nil then raise Exception.Create('Value empty!');
- if TYamlPair(aYamlArray.Items[i]).Value is TYamlObject then
- begin
- Yaml := TYamlObject(TYamlPair(aYamlArray.Items[i]).value);
- propObj := GetValue(PPByte(Result.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i, rDynArray.ElementType).AsObject;
- if propObj = nil then
- begin
- objClass := rType.TypeData.ClassType;
- rItemValue := DeserializeClass(objClass,yaml);
- end
- else
- begin
- DeserializeObject(propObj,yaml);
- end;
- end;
- end;
- tkRecord :
- begin
- Yaml := TYamlObject(TYamlPair(aYamlArray.Items[i]).value);
- rItemValue := DeserializeRecord(GetValue(PPByte(Result.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i,
- rDynArray.ElementType),aObject,Yaml);
- end;
- tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
- begin
- //skip these properties
- end
- else
- begin
- rItemValue := DeserializeType(aObject,rType.Kind,rType,aYamlArray.Items[i].Value);
- end;
- end;
- if not rItemValue.IsEmpty then Result.SetArrayElement(i,rItemValue);
- end;
- except
- on E : Exception do
- begin
- raise Exception.CreateFmt('Array %s item %d error (%s)',[rtype.Name, i, e.Message]);
- end;
- end;
- //aProperty.SetValue(aObject,rValue);
- finally
- DynArrayClear(pArr,aTypeInfo);
- end;
- end;
- {$ELSE}
- procedure TRTTIYaml.DeserializeDynArray(aTypeInfo: PTypeInfo; const aPropertyName : string; aObject: TObject; const aYamlArray: TYamlArray);
- var
- rType: PTypeInfo;
- len: NativeInt;
- pArr: Pointer;
- rItemValue: TValue;
- i: Integer;
- objClass: TClass;
- propObj : TObject;
- rValue : TValue;
- yaml : TYamlObject;
- begin
- if GetTypeData(aTypeInfo).ElType2 = nil then Exit;
- len := aYamlArray.Count;
- rType := GetTypeData(aTypeInfo).ElType2;
- pArr := nil;
- DynArraySetLength(pArr,aTypeInfo, 1, @len);
- try
- TValue.Make(@pArr,aTypeInfo, rValue);
- for i := 0 to aYamlArray.Count - 1 do
- begin
- rItemValue := nil;
- case rType.Kind of
- tkClass :
- begin
- if TYamlPair(aYamlArray.Items[i]).Value is TYamlObject then
- begin
- Yaml := TYamlObject(TYamlPair(aYamlArray.Items[i]).value);
- propObj := GetValue(PPByte(rValue.GetReferenceToRawData)^ +GetTypeData(aTypeInfo).elSize * i, GetTypeData(aTypeInfo).ElType2).AsObject;
- if propObj = nil then
- begin
- //objClass := GetTypeData(aTypeInfo).ClassType;
- objClass := GetTypeData(GetTypeData(aTypeInfo).ElType2).ClassType;
- rItemValue := DeserializeClass(objClass,yaml);
- end
- else
- begin
- DeserializeObject(propObj,yaml);
- end;
- end;
- end;
- tkRecord :
- begin
- {Yaml := TYamlObject(aYamlArray.Items[i]);
- rItemValue := DeserializeRecord(GetValue(PPByte(Result.GetReferenceToRawData)^ +rDynArray.ElementType.TypeSize * i,
- rDynArray.ElementType),aObject,Yaml); }
- end;
- tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
- begin
- //skip these properties
- end
- else
- begin
- rItemValue := DeserializeType(aObject,GetTypeData(aTypeInfo).ElType2.Kind,aPropertyName,aYamlArray.Items[i].Value);
- end;
- end;
- if not rItemValue.IsEmpty then rValue.SetArrayElement(i,rItemValue);
- end;
- //aProperty.SetValue(aObject,rValue);
- SetDynArrayProp(aObject,GetPropInfo(aObject,aPropertyName),pArr);
- finally
- DynArrayClear(pArr,aTypeInfo);
- end;
- end;
- {$ENDIF}
- {$IFNDEF FPC}
- function TRTTIYaml.DeserializeRecord(aRecord : TValue; aObject : TObject; const aYaml : TYamlObject) : TValue;
- var
- ctx : TRttiContext;
- rRec : TRttiRecordType;
- rField : TRttiField;
- rValue : TValue;
- member : TYamlPair;
- yArray : TYamlArray;
- Yaml : TYamlObject;
- objClass : TClass;
- propobj : TObject;
- begin
- rRec := ctx.GetType(aRecord.TypeInfo).AsRecord;
- try
- for rField in rRec.GetFields do
- begin
- rValue := nil;
- //member := TYamlPair(aYaml.GetValue(rField.Name));
- member := GetYamlPairByName(aYaml,rField.Name);
- if member <> nil then
- case rField.FieldType.TypeKind of
- tkDynArray :
- begin
- yArray := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlArray;
- try
- rValue := DeserializeDynArray(rField.FieldType.Handle,aObject,yArray);
- finally
- yArray.Free;
- end;
- end;
- tkClass :
- begin
- //if (member.YamlValue is TYamlObject) then
- begin
- propobj := rField.GetValue(@aRecord).AsObject;
- Yaml := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlObject;
- try
- if propobj = nil then
- begin
- objClass := rField.FieldType.Handle^.TypeData.ClassType;// aProperty.PropertyType.Handle^.TypeData.ClassType;
- rValue := DeserializeClass(objClass,Yaml);
- end
- else
- begin
- DeserializeObject(propobj,Yaml);
- end;
- finally
- Yaml.Free;
- end;
- end
- end;
- tkRecord :
- begin
- Yaml := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlObject;
- try
- rValue := DeserializeRecord(rField.GetValue(aRecord.GetReferenceToRawData),aObject,Yaml);
- finally
- Yaml.Free;
- end;
- end
- else
- begin
- //rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.ToYaml);
- {$IFNDEF FPC}
- //avoid return unicode escaped chars if string
- if rField.FieldType.TypeKind in [tkString, tkLString, tkWString, tkUString] then
- {$IFDEF DELPHIRX10_UP}
- rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.Value.AsString)
- {$ELSE}
- rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.YamlString.ToString)
- {$ENDIF}
- else rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.Value.AsString);
- {$ELSE}
- rValue := DeserializeType(aObject,rField.FieldType.TypeKind,aName,member.Value.AsString);
- {$ENDIF}
- end;
- end;
- if not rValue.IsEmpty then rField.SetValue(aRecord.GetReferenceToRawData,rValue);
- end;
- Result := aRecord;
- finally
- ctx.Free;
- end;
- end;
- {$ENDIF}
- constructor TRTTIYaml.Create(aSerializeLevel : TSerializeLevel; aUseEnumNames : Boolean = True);
- begin
- fSerializeLevel := aSerializeLevel;
- fUseEnumNames := aUseEnumNames;
- fUseYamlCaseSense := False;
- end;
- function TRTTIYaml.DeserializeClass(aType: TClass; const aYaml: TYamlObject): TObject;
- begin
- Result := nil;
- if (aYaml = nil) or ((aYaml as TYamlValue) is TYamlNull) or (aYaml.Count = 0) then Exit;
- {$IFNDEF FPC}
- Result := CreateInstance(aType).AsObject;
- {$ELSE}
- Result := aType.Create;
- {$ENDIF}
- try
- Result := DeserializeObject(Result, aYaml);
- except
- on E : Exception do
- begin
- Result.Free;
- raise EYamlDeserializeError.CreateFmt('Deserialize error class "%s" : %s',[aType.ClassName,e.Message]);
- end;
- end;
- end;
- function TRTTIYaml.DeserializeObject(aObject: TObject; const aYaml: TYamlObject): TObject;
- var
- ctx: TRttiContext;
- rType: TRttiType;
- rProp: TRttiProperty;
- {$IFNDEF FPC}
- attr: TCustomAttribute;
- {$ENDIF}
- propertyname : string;
- begin
- Result := aObject;
- if (aYaml = nil) or ((aYaml as TYamlValue) is TYamlNull) or (aYaml.Count = 0) or (Result = nil) then Exit;
- try
- rType := ctx.GetType(aObject.ClassInfo);
- try
- for rProp in rType.GetProperties do
- begin
- {$IFNDEF FPC}
- if ((fSerializeLevel = slPublicProperty) and (rProp.PropertyType.IsPublicType))
- or ((fSerializeLevel = slPublishedProperty) and ((IsPublishedProp(aObject,rProp.Name)) or (rProp.Name = 'List'))) then
- {$ENDIF}
- begin
- if ((rProp.IsWritable) or (rProp.Name = 'List')) and (IsAllowedProperty(aObject,rProp.Name)) then
- begin
- propertyname := rProp.Name;
- {$IFNDEF FPC}
- for attr in rProp.GetAttributes do if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
- if rProp.Name = 'List' then
- begin
- Result := DeserializeList(Result,propertyname,aYaml);
- end
- else if (rProp.GetValue(aObject).IsObject) and (IsGenericList(rProp.GetValue(aObject).AsObject)) then
- begin
- DeserializeList(rProp.GetValue(aObject).AsObject,'List',TYamlObject(aYaml.GetValue(propertyname)));
- end
- else if (not rProp.GetValue(aObject).IsObject) and (IsGenericXArray(string(rProp.GetValue(aObject){$IFNDEF NEXTGEN}.TypeInfo.Name{$ELSE}.TypeInfo.NameFld.ToString{$ENDIF}))) then
- begin
- DeserializeXArray(Result,rProp.GetValue(aObject),rProp,propertyname,aYaml);
- end
- else
- {$ENDIF}
- Result := DeserializeProperty(Result,propertyname,rProp,aYaml);
- end;
- end;
- end;
- finally
- ctx.Free;
- end;
- except
- on E : Exception do
- begin
- Result.Free;
- raise EYamlDeserializeError.CreateFmt('Deserialize error for object "%s" : %s',[aObject.ClassName,e.Message]);
- end;
- end;
- end;
- {$IFNDEF FPC}
- function TRTTIYaml.DeserializeList(aObject: TObject; const aName : string; const aYaml: TYamlObject) : TObject;
- var
- ctx : TRttiContext;
- rType : TRttiType;
- yArray : TYamlArray;
- member : TYamlPair;
- rvalue : TValue;
- i : Integer;
- rProp : TRttiProperty;
- {$IFDEF DELPHIRX103_UP}
- rMethod: TRttiMethod;
- n: Integer;
- {$ELSE}
- rfield : TRttiField;
- {$ENDIF}
- begin
- Result := aObject;
- rType := ctx.GetType(aObject.ClassInfo);
- try
- rProp := rType.GetProperty('List');
- if rProp = nil then Exit;
- finally
- ctx.Free;
- end;
- member := GetYamlPairByName(aYaml,aName);
- //var a := aYaml.ToYaml;
- if member = nil then yArray := TYamlArray(aYaml) //TYamlObject.ParseYamlValue(aYaml.ToYaml) as TYamlArray
- else yArray := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlArray;
- rvalue := DeserializeDynArray(rProp.PropertyType.Handle,Result,yArray);
- if not rValue.IsEmpty then
- begin
- {$IFDEF DELPHIRX103_UP}
- if (aObject <> nil) and (rvalue.IsArray) then
- begin
- rMethod := ctx.GetType(aObject.ClassType).GetMethod('Clear');
- if rMethod = nil then
- raise EYamlDeserializeError.Create('Unable to find RTTI method');
- rMethod.Invoke(aObject, []);
- rMethod := ctx.GetType(aObject.ClassType).GetMethod('Add');
- if rMethod = nil then
- raise EYamlDeserializeError.Create('Unable to find RTTI method');
- n := rvalue.GetArrayLength - 1;
- for i := 0 to n do
- rMethod.Invoke(aObject, [rvalue.GetArrayElement(i)]);
- end;
- {$ELSE}
- for rfield in rType.GetFields do
- begin
- if rfield.Name = 'FOwnsObjects' then rfield.SetValue(aObject,True);
- //if rfield.Name = 'FCount' then rfield.SetValue(aObject,i);
- if rfield.Name = 'FItems' then
- begin
- //if TList(aObject) <> nil then TList(aObject).Clear;
- //rfield.GetValue(aObject).AsObject.Free;// aValue.GetReferenceToRawData)
- rfield.SetValue(aObject,rValue);// .SetDynArrayProp(aObject,'fItems',Result);
- Break;
- end;
- end;
- rProp := rType.GetProperty('Count');
- rProp.SetValue(aObject,i);
- {$ENDIF}
- end;
- end;
- {$ENDIF}
- {$IFNDEF FPC}
- procedure TRTTIYaml.DeserializeXArray(Instance : TObject; aRecord : TValue; aProperty : TRttiProperty; const aPropertyName : string; aYaml : TYamlObject);
- var
- ctx : TRttiContext;
- rRec : TRttiRecordType;
- rfield : TRttiField;
- rValue : TValue;
- member : TYamlPair;
- yArray : TYamlArray;
- begin
- rRec := ctx.GetType(aRecord.TypeInfo).AsRecord;
- try
- rfield := rRec.GetField('fArray');
- if rfield <> nil then
- begin
- rValue := nil;
- //member := TYamlPair(aYaml.GetValue(rField.Name));
- member := GetYamlPairByName(aYaml,aPropertyName);
- if (member <> nil) and (rField.FieldType.TypeKind = tkDynArray) then
- begin
- yArray := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlArray;
- try
- rValue := DeserializeDynArray(rField.FieldType.Handle,nil,yArray);
- finally
- yArray.Free;
- end;
- end;
- end;
- if not rValue.IsEmpty then rField.SetValue(aRecord.GetReferenceToRawData,rValue);
- aProperty.SetValue(Instance,aRecord);
- finally
- ctx.Free;
- end;
- end;
- {$ENDIF}
- function TRTTIYaml.DeserializeProperty(aObject : TObject; const aName : string; aProperty : TRttiProperty; const aYaml : TYamlObject) : TObject;
- var
- rValue : TValue;
- member : TYamlPair;
- objClass: TClass;
- yArray : TYamlArray;
- Yaml : TYamlObject;
- begin
- Result := aObject;
- rValue := nil;
- //member := TYamlPair(aYaml.GetValue(aName));
- member := GetYamlPairByName(aYaml,aName);
- if member <> nil then
- begin
- case aProperty.PropertyType.TypeKind of
- tkDynArray :
- begin
- yArray := member.Value as TYamlArray;
- {$IFNDEF FPC}
- aProperty.SetValue(aObject,DeserializeDynArray(aProperty.PropertyType.Handle,Result,yArray));
- {$ELSE}
- DeserializeDynArray(aProperty.PropertyType.Handle,aName,Result,yArray);
- {$ENDIF}
- Exit;
- end;
- tkClass :
- begin
- //if (member.YamlValue is TYamlObject) then
- begin
- Yaml := TYamlObject(TYamlObject.ParseYamlValue(member.ToYaml));
- try
- if aProperty.GetValue(aObject).AsObject = nil then
- begin
- {$IFNDEF FPC}
- objClass := aProperty.PropertyType.Handle^.TypeData.ClassType;
- rValue := DeserializeClass(objClass,Yaml);
- {$ELSE}
- objClass := GetObjectPropClass(aObject,aName);
- //objClass := GetTypeData(aProperty.PropertyType.Handle)^.ClassType;
- rValue := DeserializeClass(objClass,Yaml);
- SetObjectProp(aObject,aName,rValue.AsObject);
- Exit;
- {$ENDIF}
- end
- else
- begin
- rValue := DeserializeObject(aProperty.GetValue(aObject).AsObject,Yaml);
- Exit;
- end;
- finally
- Yaml.Free;
- end;
- end
- end;
- {$IFNDEF FPC}
- tkRecord :
- begin
- Yaml := TYamlObject.ParseYamlValue(member.ToYaml) as TYamlObject;
- try
- rValue := DeserializeRecord(aProperty.GetValue(aObject),aObject,Yaml);
- finally
- Yaml.Free;
- end;
- end;
- tkSet :
- begin
- rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.ToYaml)
- end
- {$ENDIF}
- else
- begin
- {$IFNDEF FPC}
- //avoid return unicode escaped chars if string
- if aProperty.PropertyType.TypeKind in [tkString, tkLString, tkWString, tkUString] then
- {$IFDEF DELPHIRX10_UP}
- rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.Value.AsString)
- {$ELSE}
- rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.YamlString.ToString)
- {$ENDIF}
- else rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.Value.AsString);
- {$ELSE}
- rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aName,member.Value.AsString);
- if not rValue.IsEmpty then SetPropertyValue(aObject,aName,rValue);
- {$ENDIF}
- end;
- end;
- {$IFNDEF FPC}
- if not rValue.IsEmpty then aProperty.SetValue(Result,rValue);
- {$ENDIF}
- end;
- end;
- {$IFNDEF FPC}
- function TRTTIYaml.DeserializeType(aObject : TObject; aType : TTypeKind; aTypeInfo : PTypeInfo; const aValue: string) : TValue;
- var
- i : Integer;
- value : string;
- fsettings : TFormatSettings;
- begin
- try
- value := AnsiDequotedStr(aValue,'"');
- case aType of
- tkString, tkLString, tkWString, tkUString :
- begin
- Result := value;
- end;
- tkChar, tkWChar :
- begin
- Result := value;
- end;
- tkInteger :
- begin
- Result := StrToInt(value);
- end;
- tkInt64 :
- begin
- Result := StrToInt64(value);
- end;
- tkFloat :
- begin
- if aTypeInfo = TypeInfo(TDateTime) then
- begin
- if CompareText(value,'null') <> 0 then Result := JsonDateToDateTime(value);
- end
- else if aTypeInfo = TypeInfo(TDate) then
- begin
- if CompareText(value,'null') <> 0 then Result := StrToDate(value);
- end
- else if aTypeInfo = TypeInfo(TTime) then
- begin
- Result := StrToTime(value);
- end
- else
- begin
- fsettings := TFormatSettings.Create;
- Result := StrToFloat(StringReplace(value,'.',fsettings.DecimalSeparator,[]));
- end;
- end;
- tkEnumeration :
- begin
- if aTypeInfo = System.TypeInfo(Boolean) then
- begin
- Result := StrToBool(value);
- end
- else
- begin
- //if fUseEnumNames then TValue.Make(GetEnumValue(aTypeInfo,value),aTypeInfo, Result)
- // else TValue.Make(StrToInt(value),aTypeInfo, Result);
- if not TryStrToInt(value,i) then TValue.Make(GetEnumValue(aTypeInfo,value),aTypeInfo, Result)
- else TValue.Make(StrToInt(value),aTypeInfo, Result);
- end;
- end;
- tkSet :
- begin
- i := StringToSet(aTypeInfo,value);
- TValue.Make(@i,aTypeInfo,Result);
- end;
- else
- begin
- //raise EclYamlSerializerError.Create('Not supported data type!');
- end;
- end;
- except
- on E : Exception do
- begin
- raise EYamlDeserializeError.CreateFmt('Deserialize error type "%s.%s" : %s',[aObject.ClassName,GetTypeName(aTypeInfo),e.Message]);
- end;
- end;
- end;
- {$ELSE}
- function TRTTIYaml.DeserializeType(aObject : TObject; aType : TTypeKind; const aPropertyName, aValue: string) : TValue;
- var
- value : string;
- propinfo : PPropInfo;
- fsettings : TFormatSettings;
- begin
- try
- value := AnsiDequotedStr(aValue,'"');
- if value = '' then
- begin
- Result := nil;
- Exit;
- end;
- propinfo := GetPropInfo(aObject,aPropertyName);
- //case propinfo.PropType.Kind of
- case aType of
- tkString, tkLString, tkWString, tkUString, tkAString :
- begin
- Result := value;
- //SetStrProp(aObject,propinfo,value);
- end;
- tkChar, tkWChar :
- begin
- Result := value;
- end;
- tkInteger :
- begin
- Result := StrToInt(value);
- end;
- tkInt64 :
- begin
- Result := StrToInt64(value);
- end;
- tkFloat :
- begin
- if propinfo.PropType = TypeInfo(TDateTime) then
- begin
- if CompareText(value,'null') <> 0 then Result := JsonDateToDateTime(value);
- end
- else if propinfo.PropType = TypeInfo(TDate) then
- begin
- if CompareText(value,'null') <> 0 then Result := StrToDate(value);
- end
- else if propinfo.PropType = TypeInfo(TTime) then
- begin
- Result := StrToTime(value);
- end
- else
- begin
- fsettings := DefaultFormatSettings;
- Result := StrToFloat(StringReplace(value,'.',fsettings.DecimalSeparator,[]));
- end;
- end;
- tkEnumeration:
- begin
- Result := value;
- end;
- tkBool :
- begin
- Result := StrToBool(value);
- end;
- tkSet :
- begin
- Result := value;
- end;
- else
- begin
- //raise EclYamlSerializerError.Create('Not supported data type!');
- end;
- end;
- //if not Result.IsEmpty then SetPropertyValue(aObject,propinfo,Result);
- except
- on E : Exception do
- begin
- raise EYamlDeserializeError.CreateFmt('Deserialize error type "%s" : %s',[aObject.ClassName,e.Message]);
- end;
- end;
- end;
- {$ENDIF}
- function TRTTIYaml.IsAllowedProperty(aObject : TObject; const aPropertyName : string) : Boolean;
- var
- propname : string;
- begin
- Result := True;
- propname := aPropertyName.ToLower;
- if IsGenericList(aObject) then
- begin
- if (propname = 'capacity') or (propname = 'count') or (propname = 'ownsobjects') then Result := False;
- end
- else if (propname = 'refcount') then Result := False;
- end;
- function TRTTIYaml.IsGenericList(aObject : TObject) : Boolean;
- var
- cname : string;
- begin
- if aObject = nil then Exit(False);
- cname := aObject.ClassName;
- Result := (cname.StartsWith('TObjectList')) or (cname.StartsWith('TList'));
- end;
- function TRTTIYaml.IsGenericXArray(const aClassName : string) : Boolean;
- begin
- Result := aClassName.StartsWith('TXArray');
- end;
- function TRTTIYaml.GetYamlPairByName(aYaml: TYamlObject; const aName: string): TYamlPair;
- var
- candidate : TYamlPair;
- yvalue : TYamlValue;
- i : Integer;
- begin
- Result := nil;
- if fUseYamlCaseSense then
- begin
- yvalue := aYaml.GetValue(aName);
- if yvalue <> nil then Result := TYamlPair(yvalue);
- Exit;
- end
- else
- begin
- if aYaml <> nil then
- for i := 0 to aYaml.Count - 1 do
- begin
- candidate := aYaml.Pairs[I];
- if (candidate = nil) or (candidate.Value = nil) then Exit(nil);
- if CompareText(candidate.Name,aName) = 0 then
- Exit(candidate);
- end;
- end;
- Result := nil;
- end;
- //function TRTTIYaml.GetPropertyValue(Instance : TObject; const PropertyName : string) : TValue;
- //var
- // pinfo : PPropInfo;
- //begin
- // Result := nil;
- // pinfo := GetPropInfo(Instance,PropertyName);
- // case pinfo.PropType^.Kind of
- // tkInteger : Result := GetOrdProp(Instance,pinfo);
- // tkInt64 : Result := GetInt64Prop(Instance,PropertyName);
- // tkFloat : Result := GetFloatProp(Instance,PropertyName);
- // tkChar : Result := Char(GetOrdProp(Instance,PropertyName));
- // {$IFDEF FPC}
- // tkWString : Result := GetWideStrProp(Instance,PropertyName);
- // tkSString,
- // tkAString,
- // {$ELSE}
- // tkWString,
- // {$ENDIF}
- // tkLString : Result := GetStrProp(Instance,pinfo);
- // {$IFDEF FPC}
- // tkEnumeration :
- // begin
- // if fUseEnumNames then Result := GetEnumName(pinfo.PropType,GetOrdProp(Instance,PropertyName))
- // else Result := GetOrdProp(Instance,PropertyName);
- // end;
- // {$ELSE}
- // tkEnumeration :
- // begin
- // if fUseEnumNames then Result := GetEnumName(@pinfo.PropType,GetOrdProp(Instance,PropertyName))
- // else Result := GetOrdProp(Instance,PropertyName);
- // end;
- // {$ENDIF}
- // tkSet : Result := GetSetProp(Instance,pinfo,True);
- // {$IFNDEF FPC}
- // tkClass :
- // {$ELSE}
- // tkBool : Result := Boolean(GetOrdProp(Instance,pinfo));
- // tkObject :
- // {$ENDIF} Result := GetObjectProp(Instance,pinfo);
- // tkDynArray : Result := GetDynArrayProp(Instance,pinfo);
- // end;
- //end;
- function TRTTIYaml.GetPropertyValueFromObject(Instance : TObject; const PropertyName : string) : TValue;
- var
- ctx : TRttiContext;
- rprop : TRttiProperty;
- begin
- rprop := ctx.GetType(Instance.ClassInfo).GetProperty(PropertyName);
- Result := rprop.GetValue(Instance);
- end;
- {$IFNDEF FPC}
- function TRTTIYaml.GetFieldValueFromRecord(aValue : TValue; const FieldName : string) : TValue;
- var
- ctx : TRttiContext;
- rec : TRttiRecordType;
- rfield : TRttiField;
- begin
- rec := ctx.GetType(aValue.TypeInfo).AsRecord;
- rfield := rec.GetField(FieldName);
- if rfield <> nil then Result := rField.GetValue(aValue.GetReferenceToRawData)
- else Result := nil;
- end;
- {$ENDIF}
- {$IFNDEF FPC}
- function TRTTIYaml.CreateInstance(aClass: TClass): TValue;
- var
- ctx : TRttiContext;
- rtype : TRttiType;
- begin
- Result := nil;
- rtype := ctx.GetType(aClass);
- Result := CreateInstance(rtype);
- end;
- {$ENDIF}
- {$IFNDEF FPC}
- function TRTTIYaml.CreateInstance(aType: TRttiType): TValue;
- var
- rmethod : TRttiMethod;
- begin
- Result := nil;
- if atype = nil then Exit;
- for rmethod in TRttiInstanceType(atype).GetMethods do
- begin
- if rmethod.IsConstructor then
- begin
- //create if don't have parameters
- if Length(rmethod.GetParameters) = 0 then
- begin
- Result := rmethod.Invoke(TRttiInstanceType(atype).MetaclassType,[]);
- Break;
- end;
- end;
- end;
- end;
- {$ENDIF}
- {$IFDEF FPC}
- procedure TRTTIYaml.SetPropertyValue(Instance : TObject; const PropertyName : string; aValue : TValue);
- var
- pinfo : PPropInfo;
- begin
- pinfo := GetPropInfo(Instance,PropertyName);
- SetPropertyValue(Instance,pinfo,aValue);
- end;
- procedure TRTTIYaml.SetPropertyValue(Instance : TObject; aPropInfo : PPropInfo; aValue : TValue);
- begin
- case aPropInfo.PropType^.Kind of
- tkInteger : SetOrdProp(Instance,aPropInfo,aValue.AsInteger);
- tkInt64 : SetInt64Prop(Instance,aPropInfo,aValue.AsInt64);
- tkFloat : SetFloatProp(Instance,aPropInfo,aValue.AsExtended);
- tkChar : SetOrdProp(Instance,aPropInfo,aValue.AsOrdinal);
- {$IFDEF FPC}
- tkWString : SetWideStrProp(Instance,aPropInfo,aValue.AsString);
- tkSString,
- tkAString,
- {$ELSE}
- tkWString,
- {$ENDIF}
- tkLString : SetStrProp(Instance,aPropInfo,aValue.AsString);
- {$IFDEF FPC}
- tkBool : SetOrdProp(Instance,aPropInfo,aValue.AsOrdinal);
- tkSet : LoadSetProperty(Instance,aPropInfo,aValue.AsString);
- {$ENDIF}
- tkEnumeration : SetEnumProp(Instance,aPropInfo,aValue.AsString);
- {$IFNDEF FPC}
- tkClass :
- {$ELSE}
- tkObject :
- {$ENDIF} SetObjectProp(Instance,aPropInfo,aValue.AsObject);
- end;
- end;
- procedure TRTTIYaml.LoadSetProperty(aInstance : TObject; aPropInfo: PPropInfo; const aValue: string);
- type
- TCardinalSet = set of 0..SizeOf(Cardinal) * 8 - 1;
- const
- Delims = [' ', ',', '[', ']'];
- var
- TypeInfo: PTypeInfo;
- W: Cardinal;
- I, N: Integer;
- Count: Integer;
- EnumName: string;
- begin
- W := 0;
- TypeInfo := GetTypeData(GetPropType(aPropInfo))^.CompType;
- Count := WordCount(aValue, Delims);
- for N := 1 to Count do
- begin
- EnumName := ExtractWord(N, aValue, Delims);
- try
- I := GetEnumValue(TypeInfo, EnumName);
- if I >= 0 then Include(TCardinalSet(W),I);
- except
- end;
- end;
- SetOrdProp(aInstance,aPropInfo,W);
- end;
- {$ENDIF}
- function TRTTIYaml.Serialize(aObject: TObject): TYamlObject;
- var
- ctx: TRttiContext;
- {$IFNDEF FPC}
- attr : TCustomAttribute;
- comment : string;
- {$ENDIF}
- rType: TRttiType;
- rProp: TRttiProperty;
- ypair : TYamlPair;
- ExcludeSerialize : Boolean;
- propertyname : string;
- begin
- if (aObject = nil) then
- begin
- Result := nil;
- Exit;
- end;
- Result := TYamlObject.Create;
- try
- rType := ctx.GetType(aObject.ClassInfo);
- try
- //s := rType.ToString;
- for rProp in TRTTI.GetProperties(rType,roFirstBase) do
- begin
- ExcludeSerialize := False;
- propertyname := rProp.Name;
- {$IFNDEF FPC}
- comment := '';
- for attr in rProp.GetAttributes do
- begin
- if attr is TNotSerializableProperty then ExcludeSerialize := True
- else if attr is TCommentProperty then comment := TCommentProperty(attr).Comment
- else if attr is TCustomNameProperty then propertyname := TCustomNameProperty(attr).Name;
- end;
- if ((fSerializeLevel = slPublicProperty) and (rProp.PropertyType.IsPublicType))
- or ((fSerializeLevel = slPublishedProperty) and ((IsPublishedProp(aObject,rProp.Name)) or (rProp.Name = 'List'))) then
- {$ENDIF}
- begin
- if (IsAllowedProperty(aObject,propertyname)) and (not ExcludeSerialize) then
- begin
- //add comment as pair
- {$IFNDEF FPC}
- if comment <> '' then Result.AddPair(TYamlPair.Create('#',TYamlComment.Create(Comment)));
- {$ENDIF}
- begin
- if (rProp.GetValue(aObject).IsObject) and (IsGenericList(rProp.GetValue(aObject).AsObject)) then
- begin
- ypair := Serialize(propertyname,GetPropertyValueFromObject(rProp.GetValue(aObject).AsObject,'List'));
- end
- {$IFNDEF FPC}
- else if (not rProp.GetValue(aObject).IsObject) and (IsGenericXArray(string(rProp.GetValue(aObject){$IFNDEF NEXTGEN}.TypeInfo.Name{$ELSE}.TypeInfo.NameFld.ToString{$ENDIF}))) then
- begin
- ypair := Serialize(propertyname,GetFieldValueFromRecord(rProp.GetValue(aObject),'fArray'));
- end
- {$ENDIF}
- else
- begin
- {$IFNDEF FPC}
- ypair := Serialize(propertyname,rProp.GetValue(aObject));
- {$ELSE}
- ypair := Serialize(aObject,rProp.PropertyType.TypeKind,propertyname);
- {$ENDIF}
- end;
- //s := jpair.YamlValue.ToString;
- if ypair <> nil then
- begin
- Result.AddPair(ypair);
- end
- else ypair.Free;
- end;
- //Result.AddPair(Serialize(rProp.Name,rProp.GetValue(aObject)));
- //s := Result.ToYaml;
- end;
- end;
- end;
- finally
- ctx.Free;
- end;
- except
- on E : Exception do
- begin
- Result.Free;
- raise EYamlSerializeError.CreateFmt('Serialize error object "%s" : %s',[aObject.ClassName,e.Message]);
- end;
- end;
- end;
- function TRTTIYaml.GetValue(aAddr: Pointer; aType: TRTTIType): TValue;
- begin
- TValue.Make(aAddr,aType.Handle,Result);
- end;
- //function TRTTIYaml.GetValue(aAddr: Pointer; aTypeInfo: PTypeInfo): TValue;
- //begin
- // TValue.Make(aAddr,aTypeInfo,Result);
- //end;
- {$IFNDEF FPC}
- function TRTTIYaml.Serialize(const aName : string; aValue : TValue) : TYamlPair;
- var
- ctx: TRttiContext;
- rRec : TRttiRecordType;
- rField : TRttiField;
- rDynArray : TRTTIDynamicArrayType;
- Yaml : TYamlObject;
- yArray : TYamlArray;
- ypair : TYamlPair;
- yvalue : TYamlValue;
- i : Integer;
- begin
- Result := TYamlPair.Create(aName,nil);
- //Result.YamlString := TYamlString(aName);
- try
- case avalue.Kind of
- tkDynArray :
- begin
- yArray := TYamlArray.Create;
- rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
- try
- for i := 0 to aValue.GetArrayLength - 1 do
- begin
- if not GetValue(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType).IsEmpty then
- begin
- yvalue := nil;
- ypair := Serialize(aName,GetValue(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType));
- try
- //jValue := TYamlValue(jPair.YamlValue.Clone);
- yvalue := ypair.Value;
- if yvalue <> nil then
- begin
- yArray.AddElement(yvalue);
- ypair.Value.Owned := False;
- end;
- finally
- ypair.Free;
- if yvalue <> nil then yvalue.Owned := True;
- end;
- end;
- end;
- Result.Value := yArray;
- finally
- ctx.Free;
- end;
- end;
- tkClass :
- begin
- Result.Value := TYamlValue(Serialize(aValue.AsObject));
- end;
- tkInterface :
- begin
- {$IFDEF DELPHIRX10_UP}
- // Would not work with all interfaces, like iOS/Android native ones
- Result.Value := TYamlValue(Serialize(aValue.AsInterface as TObject));
- {$ENDIF}
- end;
- tkString, tkLString, tkWString, tkUString :
- begin
- Result.Value := TYamlString.Create(aValue.AsString);
- end;
- tkChar, tkWChar :
- begin
- Result.Value := TYamlString.Create(aValue.AsString);
- end;
- tkInteger :
- begin
- Result.Value := TYamlInteger.Create(aValue.AsInteger);
- end;
- tkInt64 :
- begin
- Result.Value := TYamlInteger.Create(aValue.AsInt64);
- end;
- tkFloat :
- begin
- if aValue.TypeInfo = TypeInfo(TDateTime) then
- begin
- if aValue.AsExtended <> 0.0 then Result.Value := TYamlString.Create(DateTimeToJsonDate(aValue.AsExtended));
- end
- else if aValue.TypeInfo = TypeInfo(TDate) then
- begin
- if aValue.AsExtended <> 0.0 then Result.Value := TYamlString.Create(DateToStr(aValue.AsExtended));
- end
- else if aValue.TypeInfo = TypeInfo(TTime) then
- begin
- Result.Value := TYamlString.Create(TimeToStr(aValue.AsExtended));
- end
- else
- begin
- Result.Value := TYamlFloat.Create(aValue.AsExtended);
- end;
- end;
- tkEnumeration :
- begin
- if (aValue.TypeInfo = System.TypeInfo(Boolean)) then
- begin
- Result.Value := TYamlBoolean.Create(aValue.AsBoolean);
- end
- else
- begin
- //Result.YamlValue := TYamlString.Create(GetEnumName(aValue.TypeInfo,aValue.AsOrdinal));
- if fUseEnumNames then Result.Value := TYamlString.Create(aValue.ToString)
- else Result.Value := TYamlInteger.Create(GetEnumValue(aValue.TypeInfo,aValue.ToString));
- end;
- end;
- tkSet :
- begin
- Result.Value := TYamlString.Create(aValue.ToString);
- end;
- tkRecord :
- begin
- rRec := ctx.GetType(aValue.TypeInfo).AsRecord;
- try
- Yaml := TYamlObject.Create;
- for rField in rRec.GetFields do
- begin
- Yaml.AddPair(Serialize(rField.name,rField.GetValue(aValue.GetReferenceToRawData)));
- end;
- Result.Value := Yaml;
- finally
- ctx.Free;
- end;
- end;
- tkMethod, tkPointer, tkClassRef, tkProcedure :
- begin
- //skip these properties
- //FreeAndNil(Result);
- end
- else
- begin
- raise EYamlSerializeError.CreateFmt(cNotSupportedDataType,[aName,GetTypeName(aValue.TypeInfo)]);
- end;
- end;
- if Result.Value = nil then Result.Value := TYamlNull.Create;
- except
- on E : Exception do
- begin
- Result.Free;
- raise EYamlSerializeError.CreateFmt('Serialize error class "%s.%s" : %s',[aName,aValue.ToString,e.Message]);
- end;
- end;
- end;
- {$ELSE}
- function TRTTIYaml.GetPropType(aPropInfo: PPropInfo): PTypeInfo;
- begin
- Result := aPropInfo^.PropType;
- end;
- function TRTTIYaml.FloatProperty(aObject : TObject; aPropInfo: PPropInfo): string;
- const
- Precisions: array[TFloatType] of Integer = (7, 15, 18, 18, 19);
- var
- fsettings : TFormatSettings;
- begin
- fsettings := FormatSettings;
- Result := StringReplace(FloatToStrF(GetFloatProp(aObject, aPropInfo), ffGeneral,
- Precisions[GetTypeData(GetPropType(aPropInfo))^.FloatType],0),
- '.',fsettings.DecimalSeparator,[rfReplaceAll]);
- end;
- function TRTTIYaml.Serialize(const aName : string; aValue : TValue) : TYamlPair;
- begin
- Result := TYamlPair.Create(aName,nil);
- //Result.YamlString := TYamlString(aName);
- try
- case avalue.Kind of
- tkClass :
- begin
- Result.Value := TYamlValue(Serialize(aValue.AsObject));
- end;
- tkString, tkLString, tkWString, tkUString :
- begin
- Result.Value := TYamlString.Create(aValue.AsString);
- end;
- tkChar, tkWChar :
- begin
- Result.Value := TYamlString.Create(aValue.AsString);
- end;
- tkInteger :
- begin
- Result.Value := TYamlInteger.Create(aValue.AsInteger);
- end;
- tkInt64 :
- begin
- Result.Value := TYamlInteger.Create(aValue.AsInt64);
- end;
- tkFloat :
- begin
- if aValue.TypeInfo = TypeInfo(TDateTime) then
- begin
- if aValue.AsExtended <> 0.0 then Result.Value := TYamlString.Create(DateTimeToJsonDate(aValue.AsExtended));
- end
- else if aValue.TypeInfo = TypeInfo(TDate) then
- begin
- if aValue.AsExtended <> 0.0 then Result.Value := TYamlString.Create(DateToStr(aValue.AsExtended));
- end
- else if aValue.TypeInfo = TypeInfo(TTime) then
- begin
- Result.Value := TYamlString.Create(TimeToStr(aValue.AsExtended));
- end
- else
- begin
- Result.Value := TYamlFloat.Create(aValue.AsExtended);
- end;
- end;
- tkEnumeration :
- begin
- if (aValue.TypeInfo = System.TypeInfo(Boolean)) then
- begin
- Result.Value := TYamlBoolean.Create(aValue.AsBoolean);
- end
- else
- begin
- //Result.YamlValue := TYamlString.Create(GetEnumName(aValue.TypeInfo,aValue.AsOrdinal));
- if fUseEnumNames then Result.Value := TYamlString.Create(aValue.ToString)
- else Result.Value := TYamlInteger.Create(GetEnumValue(aValue.TypeInfo,aValue.ToString));
- end;
- end;
- tkSet :
- begin
- Result.Value := TYamlString.Create(aValue.ToString);
- end;
- else
- begin
- //raise EYamlDeserializeError.CreateFmt('Not supported type "%s":%d',[aName,Integer(aValue.Kind)]);
- end;
- end;
- if Result.Value = nil then Result.Value := TYamlNull.Create;
- except
- Result.Free;
- end;
- end;
- function TRTTIYaml.Serialize(aObject : TObject; aType : TTypeKind; const aPropertyName : string) : TYamlPair;
- var
- propinfo : PPropInfo;
- yArray : TYamlArray;
- ypair : TYamlPair;
- yvalue : TYamlValue;
- i : Integer;
- pArr : Pointer;
- rValue : TValue;
- rItemValue : TValue;
- len : Integer;
- begin
- try
- Result := TYamlPair.Create(aPropertyName,nil);
- propinfo := GetPropInfo(aObject,aPropertyName);
- //case propinfo.PropType.Kind of
- case aType of
- tkDynArray :
- begin
- len := 0;
- yArray := TYamlArray.Create;
- try
- pArr := GetDynArrayProp(aObject,aPropertyName);
- TValue.Make(@pArr,propinfo.PropType, rValue);
- if rValue.IsArray then
- begin
- len := rValue.GetArrayLength;
- for i := 0 to len - 1 do
- begin
- rItemValue := rValue.GetArrayElement(i);
- ypair := Serialize(aPropertyName,rItemValue);
- try
- //jValue := TYamlValue(jPair.YamlValue.Clone);
- yvalue := ypair.Value;
- yArray.AddElement(yvalue);
- //jPair.YamlValue.Owned := False;
- finally
- ypair.Free;
- //jValue.Owned := True;
- end;
- end;
- end;
- Result.Value := yArray;
- finally
- //DynArrayClear(pArr,propinfo.PropType);
- pArr := nil;
- end;
- end;
- tkClass :
- begin
- Result.Value := TYamlValue(Serialize(GetObjectProp(aObject,aPropertyName)));
- end;
- tkString, tkLString, tkWString, tkUString, tkAString :
- begin
- Result.Value := TYamlString.Create(GetStrProp(aObject,aPropertyName));
- end;
- tkChar, tkWChar :
- begin
- Result.Value := TYamlString.Create(Char(GetOrdProp(aObject,aPropertyName)));
- end;
- tkInteger :
- begin
- Result.Value := TYamlInteger.Create(GetOrdProp(aObject,aPropertyName));
- end;
- tkInt64 :
- begin
- Result.Value := TYamlInteger.Create(GetOrdProp(aObject,aPropertyName));
- end;
- tkFloat :
- begin
- if propinfo.PropType = TypeInfo(TDateTime) then
- begin
- if aValue.AsExtended <> 0.0 then Result.Value := TYamlString.Create(DateTimeToJsonDate(GetFloatProp(aObject,aPropertyName)));
- end
- else if propinfo.PropType = TypeInfo(TDate) then
- begin
- if aValue.AsExtended <> 0.0 then Result.Value := TYamlString.Create(DateToStr(GetFloatProp(aObject,aPropertyName)));
- end
- else if propinfo.PropType = TypeInfo(TTime) then
- begin
- Result.Value := TYamlString.Create(TimeToStr(GetFloatProp(aObject,aPropertyName)));
- end
- else
- begin
- //Result.YamlValue := TYamlFloatNumber.Create(GetFloatProp(aObject,aPropertyName));
- Result.Value := TYamlFloat.Create(StrToFloat(FloatProperty(aObject,propinfo)));
- end;
- end;
- tkEnumeration,tkBool :
- begin
- if (propinfo.PropType = System.TypeInfo(Boolean)) then
- begin
- Result.Value := TYamlBoolean.Create(Boolean(GetOrdProp(aObject,aPropertyName)));
- end
- else
- begin
- if fUseEnumNames then Result.Value := TYamlString.Create(GetEnumName(propinfo.PropType,GetOrdProp(aObject,aPropertyName)))
- else Result.Value := TYamlInteger.Create(GetOrdProp(aObject,aPropertyName));
- //Result.YamlValue := TYamlString.Create(aValue.ToString);
- end;
- end;
- tkSet :
- begin
- Result.Value := TYamlString.Create(GetSetProp(aObject,aPropertyName));
- end;
- {$IFNDEF FPC}
- tkRecord :
- begin
- rRec := ctx.GetType(aValue.TypeInfo).AsRecord;
- try
- Yaml := TYamlObject.Create;
- for rField in rRec.GetFields do
- begin
- Yaml.AddPair(Serialize(rField.name,rField.GetValue(aValue.GetReferenceToRawData)));
- end;
- Result.YamlValue := Yaml;
- finally
- ctx.Free;
- end;
- end;
- {$ENDIF}
- tkMethod, tkPointer, tkClassRef ,tkInterface, tkProcedure :
- begin
- //skip these properties
- //FreeAndNil(Result);
- end
- else
- begin
- //raise EYamlDeserializeError.CreateFmt('Not supported type "%s":%d',[aName,Integer(aValue.Kind)]);
- end;
- end;
- if Result.Value = nil then Result.Value := TYamlNull.Create;
- except
- on E : Exception do
- begin
- Result.Free;
- {$IFNDEF FPC}
- raise EYamlSerializeError.CreateFmt('Serialize error class "%s.%s" : %s',[aName,aValue.ToString,e.Message]);
- {$ENDIF}
- end;
- end;
- end;
- {$ENDIF}
- { TYamlSerializer}
- constructor TYamlSerializer.Create(aSerializeLevel: TSerializeLevel; aUseEnumNames : Boolean = True);
- begin
- {$IFDEF FPC}
- if aSerializeLevel = TSerializeLevel.slPublicProperty then raise EYamlSerializeError.Create('FreePascal RTTI only supports published properties');
- {$ENDIF}
- fSerializeLevel := aSerializeLevel;
- fUseEnumNames := aUseEnumNames;
- fUseYamlCaseSense := False;
- fRTTIYaml := TRTTIYaml.Create(aSerializeLevel,aUseEnumNames);
- fRTTIYaml.UseYamlCaseSense := fUseYamlCaseSense;
- end;
- function TYamlSerializer.YamlToObject(aType: TClass; const aYaml: string): TObject;
- var
- Yaml: TYamlObject;
- begin
- Yaml := TYamlObject.ParseYamlValue(aYaml) as TYamlObject;
- try
- Result := fRTTIYaml.DeserializeClass(aType,Yaml);
- finally
- Yaml.Free;
- end;
- end;
- destructor TYamlSerializer.Destroy;
- begin
- fRTTIYaml.Free;
- inherited;
- end;
- function TYamlSerializer.YamlToObject(aObject: TObject; const aYaml: string): TObject;
- var
- Yaml: TYamlObject;
- begin
- Result := aObject;
- Yaml := TYamlObject(TYamlObject.ParseYamlValue(aYaml));
- try
- fRTTIYaml.DeserializeObject(aObject,Yaml);
- finally
- Yaml.Free;
- end;
- end;
- function TYamlSerializer.ObjectToYaml(aObject : TObject): string;
- var
- Yaml: TYamlObject;
- begin
- Yaml := fRTTIYaml.Serialize(aObject);
- try
- Result := Yaml.ToYaml;
- finally
- Yaml.Free;
- end;
- end;
- procedure TYamlSerializer.SetSerializeLevel(const Value: TSerializeLevel);
- begin
- fSerializeLevel := Value;
- if Assigned(fRTTIYaml) then fRTTIYaml.fSerializeLevel := Value;
- end;
- procedure TYamlSerializer.SetUseEnumNames(const Value: Boolean);
- begin
- fUseEnumNames := Value;
- if Assigned(fRTTIYaml) then fRTTIYaml.UseEnumNames := Value;
- end;
- procedure TYamlSerializer.SetUseYamlCaseSense(const Value: Boolean);
- begin
- fUseYamlCaseSense := Value;
- if Assigned(fRTTIYaml) then fRTTIYaml.UseYamlCaseSense := Value;
- end;
- {$IFNDEF FPC}
- { TCommentProperty }
- constructor TCommentProperty.Create(const aComment: string);
- begin
- fComment := aComment;
- end;
- { TCustomNameProperty }
- constructor TCustomNameProperty.Create(const aName: string);
- begin
- fName := aName;
- end;
- {$ENDIF}
- end.
|