{ *************************************************************************** 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.