{******************************************************************************} { } { Neon: Serialization Library for Delphi } { Copyright (c) 2018-2019 Paolo Rossi } { https://github.com/paolo-rossi/neon-library } { } {******************************************************************************} { } { 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 Neon.Core.Persistence.JSON; interface {$I Neon.inc} uses System.SysUtils, System.Classes, System.Rtti, System.SyncObjs, System.TypInfo, System.Generics.Collections, System.JSON, Neon.Core.Types, Neon.Core.Attributes, Neon.Core.Persistence, Neon.Core.DynamicTypes, Neon.Core.Utils; type /// /// JSON Serializer class /// TNeonSerializerJSON = class(TNeonBase, ISerializerContext) private /// /// Writer for members of objects and records /// procedure WriteMembers(AType: TRttiType; AInstance: Pointer; AResult: TJSONValue); private /// /// Writer for string types /// function WriteString(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for Char types /// function WriteChar(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for Boolean types /// function WriteBoolean(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for enums types
///
function WriteEnum(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for Integer types
///
function WriteInteger(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for float types /// function WriteFloat(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for TDate* types /// function WriteDate(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for Variant types /// /// /// The variant will be written as string /// function WriteVariant(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for static and dynamic arrays /// function WriteArray(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for the set type /// /// /// The output is a string with the values comma separated and enclosed by square brackets /// /// [First,Second,Third] function WriteSet(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for a record type /// /// /// For records the engine serialize the fields by default /// function WriteRecord(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for a standard TObject (descendants) type (no list, stream or streamable) /// function WriteObject(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for an Interface type /// /// /// The object that implements the interface is serialized /// function WriteInterface(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; /// /// Writer for "Enumerable" objects (Lists, Generic Lists, TStrings, etc...) /// /// /// Objects must have GetEnumerator, Clear, Add methods /// function WriteEnumerable(const AValue: TValue; ANeonObject: TNeonRttiObject; AList: IDynamicList): TJSONValue; function IsEnumerable(const AValue: TValue; out AList: IDynamicList): Boolean; /// /// Writer for "Dictionary" objects (TDictionary, TObjectDictionary) /// /// /// Objects must have Keys, Values, GetEnumerator, Clear, Add methods /// function WriteEnumerableMap(const AValue: TValue; ANeonObject: TNeonRttiObject; AMap: IDynamicMap): TJSONValue; function IsEnumerableMap(const AValue: TValue; out AMap: IDynamicMap): Boolean; /// /// Writer for "Streamable" objects /// /// /// Objects must have LoadFromStream and SaveToStream methods /// function WriteStreamable(const AValue: TValue; ANeonObject: TNeonRttiObject; AStream: IDynamicStream): TJSONValue; function IsStreamable(const AValue: TValue; out AStream: IDynamicStream): Boolean; /// /// Writer for "Nullable" records /// /// /// Record must have HasValue and GetValue methods /// function WriteNullable(const AValue: TValue; ANeonObject: TNeonRttiObject; ANullable: IDynamicNullable): TJSONValue; function IsNullable(const AValue: TValue; out ANullable: IDynamicNullable): Boolean; protected /// /// Function to be called by a custom serializer method (ISerializeContext) /// function WriteDataMember(const AValue: TValue): TJSONValue; overload; /// /// This method chooses the right Writer based on the Kind of the AValue parameter /// function WriteDataMember(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; overload; public constructor Create(const AConfig: INeonConfiguration); /// /// Serialize any Delphi type into a JSONValue, the Delphi type must be passed as a TValue /// function ValueToJSON(const AValue: TValue): TJSONValue; /// /// Serialize any Delphi objects into a JSONValue /// function ObjectToJSON(AObject: TObject): TJSONValue; end; TNeonDeserializerParam = record JSONValue: TJSONValue; RttiType: TRttiType; NeonObject: TNeonRttiObject; procedure Default; end; /// /// JSON Deserializer class /// TNeonDeserializerJSON = class(TNeonBase, IDeserializerContext) private procedure ReadMembers(AType: TRttiType; AInstance: Pointer; AJSONObject: TJSONObject); private function ReadString(const AParam: TNeonDeserializerParam): TValue; function ReadChar(const AParam: TNeonDeserializerParam): TValue; function ReadEnum(const AParam: TNeonDeserializerParam): TValue; function ReadInteger(const AParam: TNeonDeserializerParam): TValue; function ReadInt64(const AParam: TNeonDeserializerParam): TValue; function ReadFloat(const AParam: TNeonDeserializerParam): TValue; function ReadSet(const AParam: TNeonDeserializerParam): TValue; function ReadVariant(const AParam: TNeonDeserializerParam): TValue; private function ReadArray(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; function ReadDynArray(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; function ReadObject(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; function ReadInterface(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; function ReadRecord(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; // Dynamic types function ReadStreamable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; function ReadEnumerable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; function ReadEnumerableMap(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; function ReadNullable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; private function ReadDataMember(AJSONValue: TJSONValue; AType: TRttiType; const AData: TValue): TValue; overload; function ReadDataMember(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; overload; public constructor Create(const AConfig: INeonConfiguration); procedure JSONToObject(AObject: TObject; AJSON: TJSONValue); function JSONToTValue(AJSON: TJSONValue; AType: TRttiType): TValue; overload; function JSONToTValue(AJSON: TJSONValue; AType: TRttiType; const AData: TValue): TValue; overload; function JSONToArray(AJSON: TJSONValue; AType: TRttiType): TValue; end; /// /// Static utility class for serializing and deserializing Delphi types /// TNeon = class private /// /// Prints a TJSONValue in a single line or formatted (PrettyPrinting) /// class procedure PrintToWriter(AJSONValue: TJSONValue; AWriter: TTextWriter; APretty: Boolean); static; public /// /// Prints a TJSONValue in a single line or formatted (PrettyPrinting) /// class function Print(AJSONValue: TJSONValue; APretty: Boolean): string; static; /// /// Prints a TJSONValue in a single line or formatted (PrettyPrinting) /// class procedure PrintToStream(AJSONValue: TJSONValue; AStream: TStream; APretty: Boolean); static; public /// /// Serializes a value based type (record, string, integer, etc...) to a TStream /// class procedure ValueToStream(const AValue: TValue; AStream: TStream); overload; /// /// Serializes a value based type (record, string, integer, etc...) to a TStream with a given configuration /// class procedure ValueToStream(const AValue: TValue; AStream: TStream; AConfig: INeonConfiguration); overload; /// /// Serializes a value based type (record, string, integer, etc...) to a /// TJSONValue with a default configuration /// class function ValueToJSON(const AValue: TValue): TJSONValue; overload; /// /// Serializes a value based type (record, string, integer, etc...) to a TJSONValue /// with a given configuration /// class function ValueToJSON(const AValue: TValue; AConfig: INeonConfiguration): TJSONValue; overload; public /// /// Serializes an object based type into a TTStream with a default configuration /// class procedure ObjectToStream(AObject: TObject; AStream: TStream); overload; /// /// Serializes an object based type into a TTStream with a given configuration /// class procedure ObjectToStream(AObject: TObject; AStream: TStream; AConfig: INeonConfiguration); overload; /// /// Serializes an object based type to a TJSONValue with a default configuration /// class function ObjectToJSON(AObject: TObject): TJSONValue; overload; /// /// Serializes an object based type to a TJSONValue with a given configuration
///
class function ObjectToJSON(AObject: TObject; AConfig: INeonConfiguration): TJSONValue; overload; /// /// Serializes an object based type to a string with a default configuration
///
class function ObjectToJSONString(AObject: TObject): string; overload; /// /// Serializes an object based type to a string with a given configuration
///
class function ObjectToJSONString(AObject: TObject; AConfig: INeonConfiguration): string; overload; public /// /// Deserializes a TJSONValue into a TObject with a given configuration /// class procedure JSONToObject(AObject: TObject; AJSON: TJSONValue; AConfig: INeonConfiguration); overload; /// /// Deserializes a string into a TObject with a given configuration /// class procedure JSONToObject(AObject: TObject; const AJSON: string; AConfig: INeonConfiguration); overload; /// /// Deserializes a TJSONValue into a TRttiType with a default configuration /// class function JSONToObject(AType: TRttiType; AJSON: TJSONValue): TObject; overload; /// /// Deserializes a TJSONValue into a TRttiType with a given configuration /// class function JSONToObject(AType: TRttiType; AJSON: TJSONValue; AConfig: INeonConfiguration): TObject; overload; /// /// Deserializes a string into a TRttiType with a default configuration /// class function JSONToObject(AType: TRttiType; const AJSON: string): TObject; overload; /// /// Deserializes a string into a TRttiType with a given configuration /// class function JSONToObject(AType: TRttiType; const AJSON: string; AConfig: INeonConfiguration): TObject; overload; /// /// Deserializes a TJSONValue into a generic type <T> with a default /// configuration /// class function JSONToObject(AJSON: TJSONValue): T; overload; /// /// Deserializes a TJSONValue into a generic type <T> with a given /// configuration
///
class function JSONToObject(AJSON: TJSONValue; AConfig: INeonConfiguration): T; overload; /// /// Deserializes a string into a generic type <T> with a default /// configuration
///
class function JSONToObject(const AJSON: string): T; overload; /// /// Deserializes a string into a generic type <T> with a given configuration
///
class function JSONToObject(const AJSON: string; AConfig: INeonConfiguration): T; overload; public /// /// Deserializes a TJSONValue into a TRttiType value based with a default /// configuration
///
class function JSONToValue(ARttiType: TRttiType; AJSON: TJSONValue): TValue; overload; /// /// Deserializes a TJSONValue into a TRttiType value based with a given /// configuration /// class function JSONToValue(ARttiType: TRttiType; AJSON: TJSONValue; AConfig: INeonConfiguration): TValue; overload; /// /// Deserializes a TJSONValue into a generic type <T> (value based) with a /// default configuration /// class function JSONToValue(AJSON: TJSONValue): T; overload; /// /// Deserializes a TJSONValue into a generic type <T> (value based) with a /// given configuration
///
class function JSONToValue(AJSON: TJSONValue; AConfig: INeonConfiguration): T; overload; end; implementation uses System.DateUtils, System.Variants; { TNeonSerializerJSON } constructor TNeonSerializerJSON.Create(const AConfig: INeonConfiguration); begin inherited Create(AConfig); FOperation := TNeonOperation.Serialize; end; function TNeonSerializerJSON.IsEnumerable(const AValue: TValue; out AList: IDynamicList): Boolean; begin AList := TDynamicList.GuessType(AValue.AsObject); Result := Assigned(AList); end; function TNeonSerializerJSON.IsEnumerableMap(const AValue: TValue; out AMap: IDynamicMap): Boolean; begin AMap := TDynamicMap.GuessType(AValue.AsObject); Result := Assigned(AMap); end; function TNeonSerializerJSON.IsNullable(const AValue: TValue; out ANullable: IDynamicNullable): Boolean; begin ANullable := TDynamicNullable.GuessType(AValue); Result := Assigned(ANullable); end; function TNeonSerializerJSON.IsStreamable(const AValue: TValue; out AStream: IDynamicStream): Boolean; begin AStream := TDynamicStream.GuessType(AValue.AsObject); Result := Assigned(AStream); end; function TNeonSerializerJSON.ObjectToJSON(AObject: TObject): TJSONValue; begin FOriginalInstance := AObject; if not Assigned(AObject) then Exit(TJSONObject.Create); Result := WriteDataMember(AObject); end; function TNeonSerializerJSON.ValueToJSON(const AValue: TValue): TJSONValue; begin FOriginalInstance := AValue; Result := WriteDataMember(AValue); end; function TNeonSerializerJSON.WriteArray(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LIndex, LCount: Integer; LArray: TJSONArray; begin LCount := AValue.GetArrayLength; if ANeonObject.NeonInclude.Value = IncludeIf.NotEmpty then if LCount = 0 then Exit(nil); LArray := TJSONArray.Create; for LIndex := 0 to LCount - 1 do LArray.AddElement(WriteDataMember(AValue.GetArrayElement(LIndex))); Result := LArray; end; function TNeonSerializerJSON.WriteBoolean(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; begin Result := TJSONBool.Create(AValue.AsBoolean); end; function TNeonSerializerJSON.WriteChar(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LStr: string; begin LStr := AValue.AsString; case ANeonObject.NeonInclude.Value of IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if (LStr = #0) or LStr.IsEmpty then Exit(nil); end; end; if (LStr = #0) or LStr.IsEmpty then Result := TJSONString.Create('') else Result := TJSONString.Create(AValue.AsString); end; function TNeonSerializerJSON.WriteDataMember(const AValue: TValue): TJSONValue; var LNeonObject: TNeonRttiObject; LRttiType: TRttiType; begin LRttiType := TRttiUtils.Context.GetType(AValue.TypeInfo); LNeonObject := TNeonRttiObject.Create(LRttiType, FOperation); LNeonObject.ParseAttributes; try Result := WriteDataMember(AValue, LNeonObject); finally LNeonObject.Free; end; end; function TNeonSerializerJSON.WriteDataMember(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LCustomSer: TCustomSerializer; LDynamicType: IDynamicType; LDynamicMap: IDynamicMap absolute LDynamicType; LDynamicList: IDynamicList absolute LDynamicType; LDynamicStream: IDynamicStream absolute LDynamicType; LDynamicNullable: IDynamicNullable absolute LDynamicType; begin Result := nil; LCustomSer := FConfig.Serializers.GetSerializer(AValue.TypeInfo); if Assigned(LCustomSer) then begin Result := LCustomSer.Serialize(AValue, ANeonObject, Self); Exit(Result); end; case AValue.Kind of tkChar, tkWChar: begin Result := WriteChar(AValue, ANeonObject); end; tkString, tkLString, tkWString, tkUString: begin Result := WriteString(AValue, ANeonObject); end; tkEnumeration: begin if AValue.TypeInfo = System.TypeInfo(Boolean) then Result := WriteBoolean(AValue, ANeonObject) else Result := WriteEnum(AValue, ANeonObject); end; tkInteger, tkInt64: begin Result := WriteInteger(AValue, ANeonObject); end; tkFloat: begin if (AValue.TypeInfo = System.TypeInfo(TDateTime)) or (AValue.TypeInfo = System.TypeInfo(TDate)) or (AValue.TypeInfo = System.TypeInfo(TTime)) then Result := WriteDate(AValue, ANeonObject) else Result := WriteFloat(AValue, ANeonObject); end; tkClass: begin if AValue.AsObject = nil then begin case ANeonObject.NeonInclude.Value of IncludeIf.Always, IncludeIf.CustomFunction: Exit(TJSONNull.Create); else Exit(nil); end; end else if IsEnumerableMap(AValue, LDynamicMap) then Result := WriteEnumerableMap(AValue, ANeonObject, LDynamicMap) else if IsEnumerable(AValue, LDynamicList) then Result := WriteEnumerable(AValue, ANeonObject, LDynamicList) else if IsStreamable(AValue, LDynamicStream) then Result := WriteStreamable(AValue, ANeonObject, LDynamicStream) else Result := WriteObject(AValue, ANeonObject); end; tkArray: begin Result := WriteArray(AValue, ANeonObject); end; tkDynArray: begin Result := WriteArray(AValue, ANeonObject); end; tkSet: begin Result := WriteSet(AValue, ANeonObject); end; tkRecord{$IFDEF HAS_MRECORDS}, tkMRecord{$ENDIF}: begin if IsNullable(AValue, LDynamicNullable) then Result := WriteNullable(AValue, ANeonObject, LDynamicNullable) else Result := WriteRecord(AValue, ANeonObject); end; tkInterface: begin Result := WriteInterface(AValue, ANeonObject); end; tkVariant: begin Result := WriteVariant(AValue, ANeonObject); end; { tkUnknown, tkMethod, tkPointer, tkProcedure, tkClassRef: } end; end; function TNeonSerializerJSON.WriteDate(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; begin case ANeonObject.NeonInclude.Value of IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if AValue.AsExtended = 0 then Exit(nil); end; end; Result := TJSONString.Create(TJSONUtils.DateToJSON(AValue.AsType, FConfig.UseUTCDate)) end; function TNeonSerializerJSON.WriteEnum(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LValue: Int64; LTypeData: PTypeData; LName: string; begin LName := ''; LValue := AValue.AsOrdinal; LTypeData := GetTypeData(AValue.TypeInfo); if (LValue >= LTypeData.MinValue) and (LValue <= LTypeData.MaxValue) then begin LName := GetEnumName(AValue.TypeInfo, LValue); if Length(ANeonObject.NeonEnumNames) > 0 then begin if (LValue >= Low(ANeonObject.NeonEnumNames)) and (LValue <= High(ANeonObject.NeonEnumNames)) then LName := ANeonObject.NeonEnumNames[LValue] end; Result := TJSONString.Create(LName); end else raise ENeonException.Create('Enum value out of bound: ' + LValue.ToString); end; function TNeonSerializerJSON.WriteFloat(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; begin case ANeonObject.NeonInclude.Value of IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if AValue.AsExtended = 0 then Exit(nil); end; end; Result := TJSONNumber.Create(AValue.AsExtended); end; function TNeonSerializerJSON.WriteInteger(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; begin case ANeonObject.NeonInclude.Value of IncludeIf.NotDefault: begin if AValue.AsInt64 = 0 then Exit(nil); end; end; Result := TJSONNumber.Create(AValue.AsInt64); end; function TNeonSerializerJSON.WriteInterface(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LInterface: IInterface; LObject: TObject; begin LInterface := AValue.AsInterface; LObject := LInterface as TObject; Result := WriteObject(LObject, ANeonObject); end; procedure TNeonSerializerJSON.WriteMembers(AType: TRttiType; AInstance: Pointer; AResult: TJSONValue); var LJSONValue: TJSONValue; LMembers: TNeonRttiMembers; LNeonMember: TNeonRttiMember; begin LMembers := GetNeonMembers(AInstance, AType); LMembers.FilterSerialize; try for LNeonMember in LMembers do begin if LNeonMember.Serializable then begin try LJSONValue := WriteDataMember(LNeonMember.GetValue, LNeonMember); if Assigned(LJSONValue) then (AResult as TJSONObject).AddPair(GetNameFromMember(LNeonMember), LJSONValue); except on E: Exception do begin LogError(Format('Error converting member [%s] of type [%s]: %s', [LNeonMember.Name, AType.Name, E.Message])); end; end; end; end; finally LMembers.Free; end; end; function TNeonSerializerJSON.WriteNullable(const AValue: TValue; ANeonObject: TNeonRttiObject; ANullable: IDynamicNullable): TJSONValue; begin Result := nil; if not Assigned(ANullable) then Exit; case ANeonObject.NeonInclude.Value of IncludeIf.Always, IncludeIf.CustomFunction: begin if ANullable.HasValue then Result := WriteDataMember(ANullable.GetValue, ANeonObject) else Result := TJSONNull.Create; end; IncludeIf.NotNull, IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if ANullable.HasValue then Result := WriteDataMember(ANullable.GetValue, ANeonObject); end; end; end; function TNeonSerializerJSON.WriteObject(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LObject: TObject; LType: TRttiType; begin LObject := AValue.AsObject; if LObject = nil then Exit(nil); LType := TRttiUtils.Context.GetType(LObject.ClassType); Result := TJSONObject.Create; try WriteMembers(LType, LObject, Result); case ANeonObject.NeonInclude.Value of IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if (Result as TJSONObject).Count = 0 then FreeAndNil(Result); end; end; except FreeAndNil(Result); end; end; function TNeonSerializerJSON.WriteEnumerable(const AValue: TValue; ANeonObject: TNeonRttiObject; AList: IDynamicList): TJSONValue; var LJSONValue: TJSONValue; begin // Not an enumerable object if not Assigned(AList) then Exit(nil); if ANeonObject.NeonInclude.Value = IncludeIf.NotEmpty then if AList.Count = 0 then Exit(nil); Result := TJSONArray.Create; while AList.MoveNext do begin LJSONValue := WriteDataMember(AList.Current); (Result as TJSONArray).AddElement(LJSONValue); end; end; function TNeonSerializerJSON.WriteEnumerableMap(const AValue: TValue; ANeonObject: TNeonRttiObject; AMap: IDynamicMap): TJSONValue; var LName: string; LJSONName: TJSONValue; LJSONValue: TJSONValue; LKeyValue, LValValue: TValue; begin // Not an EnumerableMap object if not Assigned(AMap) then Exit(nil); case ANeonObject.NeonInclude.Value of IncludeIf.Always: begin if not Assigned(AMap) then Exit(TJSONNull.Create); end; IncludeIf.NotNull: begin if not Assigned(AMap) then Exit(nil); end; IncludeIf.NotEmpty: begin if AMap.Count = 0 then Exit(nil); end; IncludeIf.NotDefault: ; end; Result := TJSONObject.Create; try while AMap.MoveNext do begin LKeyValue := AMap.CurrentKey; LValValue := AMap.CurrentValue; LJSONName := WriteDataMember(LKeyValue); try LJSONValue := WriteDataMember(LValValue); if LJSONName is TJSONString then LName := (LJSONName as TJSONString).Value else if AMap.KeyIsString then LName := AMap.KeyToString(LKeyValue); (Result as TJSONObject).AddPair(LName, LJSONValue); if LName.IsEmpty then raise ENeonException.Create('Dictionary [Key]: type not supported'); finally LJSONName.Free; end; end; except on E: Exception do begin FErrors.Add(E.Message); FreeAndNil(Result); end; end; end; function TNeonSerializerJSON.WriteRecord(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LType: TRttiType; begin Result := TJSONObject.Create; LType := TRttiUtils.Context.GetType(AValue.TypeInfo); try WriteMembers(LType, AValue.GetReferenceToRawData, Result); case ANeonObject.NeonInclude.Value of IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if ANeonObject.NeonInclude.Value = IncludeIf.NotEmpty then if (Result as TJSONObject).Count = 0 then FreeAndNil(Result); end; end; except FreeAndNil(Result); end; end; function TNeonSerializerJSON.WriteSet(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LRes: string; begin LRes := SetToString(AValue.TypeInfo, Integer(AValue.GetReferenceToRawData^), True); if ANeonObject.NeonInclude.Value = IncludeIf.NotEmpty then if LRes = '[]' then Exit(nil); Result := TJSONString.Create(LRes); end; function TNeonSerializerJSON.WriteStreamable(const AValue: TValue; ANeonObject: TNeonRttiObject; AStream: IDynamicStream): TJSONValue; var LBinaryStream: TMemoryStream; LBase64: string; begin Result := nil; if Assigned(AStream) then begin LBinaryStream := TMemoryStream.Create; try AStream.SaveToStream(LBinaryStream); LBinaryStream.Position := soFromBeginning; LBase64 := TBase64.Encode(LBinaryStream); if IsOriginalInstance(AValue) then Result := TJSONObject.Create.AddPair('$value', LBase64) else Result := TJSONString.Create(LBase64); finally LBinaryStream.Free; end; end; end; function TNeonSerializerJSON.WriteString(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; begin case ANeonObject.NeonInclude.Value of IncludeIf.NotEmpty, IncludeIf.NotDefault: begin if AValue.AsString.IsEmpty then Exit(nil); end; end; Result := TJSONString.Create(AValue.AsString); end; function TNeonSerializerJSON.WriteVariant(const AValue: TValue; ANeonObject: TNeonRttiObject): TJSONValue; var LValue: Variant; LVariantType: Integer; begin LValue := AValue.AsVariant; case ANeonObject.NeonInclude.Value of IncludeIf.Always: begin if VarIsNull(LValue) then Exit(TJSONNull.Create); end; IncludeIf.NotNull: begin if VarIsNull(LValue) then Exit(nil); end; IncludeIf.NotEmpty: begin if VarIsEmpty(LValue) then Exit(nil); end; end; LVariantType := VarType(LValue) and VarTypeMask; case LVariantType of //varEmpty : //varNull : varSmallInt, varInteger : Result := WriteInteger(Int64(LValue), ANeonObject); varSingle , varDouble , varCurrency: Result := WriteFloat(Currency(LValue), ANeonObject); varDate : Result := WriteDate(VarToDateTime(LValue), ANeonObject); //varOleStr : //varDispatch: //varError : varBoolean : Result := WriteBoolean(Boolean(LValue), ANeonObject); //varVariant : //varUnknown : varByte , varWord , varLongWord, varInt64 : Result := WriteInteger(Int64(LValue), ANeonObject); //varStrArg : varString : Result := WriteString(VarToStr(LValue), ANeonObject); //varAny : //varTypeMask: else Result := TJSONString.Create(AValue.AsVariant); end; end; { TNeonDeserializerJSON } constructor TNeonDeserializerJSON.Create(const AConfig: INeonConfiguration); begin inherited Create(AConfig); FOperation := TNeonOperation.Deserialize; end; function TNeonDeserializerJSON.ReadArray(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; var LIndex: NativeInt; LItemValue: TValue; LJSONArray: TJSONArray; LParam: TNeonDeserializerParam; begin // TValue record copy (but the TValue only copy the reference to Data) Result := AData; LParam.NeonObject := AParam.NeonObject; // Clear (and Free) previous elements? LJSONArray := AParam.JSONValue as TJSONArray; LParam.RttiType := (AParam.RttiType as TRttiArrayType).ElementType; // Check static array bounds for LIndex := 0 to LJSONArray.Count - 1 do begin LParam.JSONValue := LJSONArray.Items[LIndex]; LItemValue := TRttiUtils.CreateNewValue(LParam.RttiType); LItemValue := ReadDataMember(LParam, Result); Result.SetArrayElement(LIndex, LItemValue); end; end; function TNeonDeserializerJSON.ReadDynArray(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; var LIndex: NativeInt; LItemValue: TValue; LArrayLength: NativeInt; LJSONArray: TJSONArray; LParam: TNeonDeserializerParam; begin Result := AData; LParam.NeonObject := AParam.NeonObject; // Clear (and Free) previous elements? LJSONArray := AParam.JSONValue as TJSONArray; LParam.RttiType := (AParam.RttiType as TRttiDynamicArrayType).ElementType; LArrayLength := LJSONArray.Count; DynArraySetLength(PPointer(Result.GetReferenceToRawData)^, Result.TypeInfo, 1, @LArrayLength); for LIndex := 0 to LJSONArray.Count - 1 do begin LParam.JSONValue := LJSONArray.Items[LIndex]; LItemValue := TRttiUtils.CreateNewValue(LParam.RttiType); LItemValue := ReadDataMember(LParam, LItemValue); Result.SetArrayElement(LIndex, LItemValue); end; end; function TNeonDeserializerJSON.ReadChar(const AParam: TNeonDeserializerParam): TValue; begin if (AParam.JSONValue is TJSONNull) or AParam.JSONValue.Value.IsEmpty then Exit(#0); case AParam.RttiType.TypeKind of // AnsiChar tkChar: Result := TValue.From(UTF8Char(AParam.JSONValue.Value.Chars[0])); // WideChar tkWChar: Result := TValue.From(AParam.JSONValue.Value.Chars[0]); end; end; function TNeonDeserializerJSON.ReadDataMember(AJSONValue: TJSONValue; AType: TRttiType; const AData: TValue): TValue; var LParam: TNeonDeserializerParam; begin LParam.JSONValue := AJSONValue; LParam.RttiType := AType; LParam.NeonObject := TNeonRttiObject.Create(AType, FOperation); LParam.NeonObject.ParseAttributes; try Result := ReadDataMember(LParam, AData); finally LParam.NeonObject.Free; end; end; function TNeonDeserializerJSON.ReadDataMember(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; var LCustom: TCustomSerializer; begin if AParam.JSONValue is TJSONNull then Exit(TValue.Empty); // if there is a custom serializer LCustom := FConfig.Serializers.GetSerializer(AParam.RttiType.Handle); if Assigned(LCustom) then begin Result := LCustom.Deserialize(AParam.JSONValue, AData, AParam.NeonObject, Self); Exit(Result); end; case AParam.RttiType.TypeKind of // Simple types tkInt64: Result := ReadInt64(AParam); tkInteger: Result := ReadInteger(AParam); tkChar: Result := ReadChar(AParam); tkWChar: Result := ReadChar(AParam); tkEnumeration: Result := ReadEnum(AParam); tkFloat: Result := ReadFloat(AParam); tkLString: Result := ReadString(AParam); tkWString: Result := ReadString(AParam); tkUString: Result := ReadString(AParam); tkString: Result := ReadString(AParam); tkSet: Result := ReadSet(AParam); tkVariant: Result := ReadVariant(AParam); tkArray: Result := ReadArray(AParam, AData); tkDynArray: Result := ReadDynArray(AParam, AData); // Complex types tkClass: begin if ReadEnumerableMap(AParam, AData) then Result := AData else if ReadEnumerable(AParam, AData) then Result := AData else if ReadStreamable(AParam, AData) then Result := AData else Result := ReadObject(AParam, AData); end; tkInterface: Result := ReadInterface(AParam, AData); tkRecord{$IFDEF HAS_MRECORDS}, tkMRecord{$ENDIF}: begin if ReadNullable(AParam, AData) then Result := AData else Result := ReadRecord(AParam, AData); end; // Not supported (yet) { tkUnknown: ; tkClassRef: ; tkPointer: ; tkMethod: ; tkProcedure: ; } else Result := TValue.Empty; end; end; function TNeonDeserializerJSON.ReadEnum(const AParam: TNeonDeserializerParam): TValue; var LIndex, LOrdinal: Integer; LTypeData: PTypeData; begin if AParam.RttiType.Handle = System.TypeInfo(Boolean) then begin if AParam.JSONValue is TJSONTrue then Result := True else if AParam.JSONValue is TJSONFalse then Result := False else raise ENeonException.Create('Invalid JSON value. Boolean expected'); end else begin LOrdinal := -1; if Length(AParam.NeonObject.NeonEnumNames) > 0 then begin for LIndex := Low(AParam.NeonObject.NeonEnumNames) to High(AParam.NeonObject.NeonEnumNames) do if AParam.JSONValue.Value = AParam.NeonObject.NeonEnumNames[LIndex] then LOrdinal := LIndex; end; if LOrdinal = -1 then LOrdinal := GetEnumValue(AParam.RttiType.Handle, AParam.JSONValue.Value); LTypeData := GetTypeData(AParam.RttiType.Handle); if (LOrdinal >= LTypeData.MinValue) and (LOrdinal <= LTypeData.MaxValue) then TValue.Make(LOrdinal, AParam.RttiType.Handle, Result) else raise ENeonException.Create('No correspondence with enum names'); end; end; function TNeonDeserializerJSON.ReadEnumerable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; var LItemValue: TValue; LList: IDynamicList; LJSONArray: TJSONArray; LIndex: Integer; LParam: TNeonDeserializerParam; begin Result := False; LParam.NeonObject := AParam.NeonObject; LList := TDynamicList.GuessType(AData.AsObject); if Assigned(LList) then begin Result := True; LParam.RttiType := LList.GetItemType; LList.Clear; LJSONArray := AParam.JSONValue as TJSONArray; for LIndex := 0 to LJSONArray.Count - 1 do begin LParam.JSONValue := LJSONArray.Items[LIndex]; LItemValue := LList.NewItem; LItemValue := ReadDataMember(LParam, LItemValue); LList.Add(LItemValue); end; end; end; function TNeonDeserializerJSON.ReadEnumerableMap(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; var LMap: IDynamicMap; {$IFDEF HAS_NEW_JSON} LEnum: TJSONObject.TEnumerator; {$ELSE} LEnum: TJSONPairEnumerator; {$ENDIF} LKey, LValue: TValue; LParamKey, LParamValue: TNeonDeserializerParam; begin Result := False; LParamKey.NeonObject := AParam.NeonObject; LParamValue.NeonObject := AParam.NeonObject; LMap := TDynamicMap.GuessType(AData.AsObject); if Assigned(LMap) then begin Result := True; LParamKey.RttiType := LMap.GetKeyType; LParamValue.RttiType := LMap.GetValueType; LMap.Clear; LEnum := (AParam.JSONValue as TJSONObject).GetEnumerator; try while LEnum.MoveNext do begin LKey := LMap.NewKey; LParamKey.JSONValue := LEnum.Current.JsonString; if LParamKey.RttiType.TypeKind = tkClass then LMap.KeyFromString(LKey, LEnum.Current.JsonString.Value) else LKey := ReadDataMember(LParamKey, LKey); LValue := LMap.NewValue; LParamValue.JSONValue := LEnum.Current.JsonValue; LValue := ReadDataMember(LParamValue, LValue); LMap.Add(LKey, LValue); end; finally LEnum.Free; end; end; end; function TNeonDeserializerJSON.ReadFloat(const AParam: TNeonDeserializerParam): TValue; begin if AParam.JSONValue is TJSONNull then Exit(0); if AParam.RttiType.Handle = System.TypeInfo(TDate) then Result := TValue.From(TJSONUtils.JSONToDate(AParam.JSONValue.Value, True)) else if AParam.RttiType.Handle = System.TypeInfo(TTime) then Result := TValue.From(TJSONUtils.JSONToDate(AParam.JSONValue.Value, True)) else if AParam.RttiType.Handle = System.TypeInfo(TDateTime) then Result := TValue.From(TJSONUtils.JSONToDate(AParam.JSONValue.Value, FConfig.UseUTCDate)) else begin if AParam.JSONValue is TJSONNumber then Result := (AParam.JSONValue as TJSONNumber).AsDouble else raise ENeonException.Create('Invalid JSON value. Float expected'); end; end; function TNeonDeserializerJSON.ReadInt64(const AParam: TNeonDeserializerParam): TValue; var LNumber: TJSONNumber; begin if AParam.JSONValue is TJSONNull then Exit(0); LNumber := AParam.JSONValue as TJSONNumber; Result := LNumber.AsInt64 end; function TNeonDeserializerJSON.ReadInteger(const AParam: TNeonDeserializerParam): TValue; var LNumber: TJSONNumber; begin if AParam.JSONValue is TJSONNull then Exit(0); LNumber := AParam.JSONValue as TJSONNumber; Result := LNumber.AsInt; end; function TNeonDeserializerJSON.ReadInterface(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; begin Result := AData; end; procedure TNeonDeserializerJSON.ReadMembers(AType: TRttiType; AInstance: Pointer; AJSONObject: TJSONObject); var LMembers: TNeonRttiMembers; LNeonMember: TNeonRttiMember; LMemberValue: TValue; LParam: TNeonDeserializerParam; begin LMembers := GetNeonMembers(AInstance, AType); LMembers.FilterDeserialize; try for LNeonMember in LMembers do begin if LNeonMember.Serializable then begin LParam.NeonObject := LNeonMember; LParam.RttiType := LNeonMember.RttiType; //Look for a JSON with the calculated Member Name LParam.JSONValue := AJSONObject.GetValue(GetNameFromMember(LNeonMember)); // Property not found in JSON, continue to the next one if not Assigned(LParam.JSONValue) then Continue; try LMemberValue := ReadDataMember(LParam, LNeonMember.GetValue); LNeonMember.SetValue(LMemberValue); except on E: Exception do begin LogError(Format('Error converting member [%s] of type [%s]: %s', [LNeonMember.Name, AType.Name, E.Message])); end; end; end; end; finally LMembers.Free; end; end; function TNeonDeserializerJSON.ReadNullable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; var LNullable: IDynamicNullable; LValue: TValue; LValueType: TRttiType; begin Result := False; LNullable := TDynamicNullable.GuessType(AData); if Assigned(LNullable) then begin Result := True; LValueType := TRttiUtils.Context.GetType(LNullable.GetValueType); LValue := JSONToTValue(AParam.JSONValue, LValueType); LNullable.SetValue(LValue); end; end; function TNeonDeserializerJSON.ReadObject(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; var LJSONObject: TJSONObject; LPData: Pointer; begin Result := AData; LPData := AData.AsObject; if not Assigned(LPData) then Exit; LJSONObject := AParam.JSONValue as TJSONObject; if (AParam.RttiType.TypeKind = tkClass) or (AParam.RttiType.TypeKind = tkInterface) then ReadMembers(AParam.RttiType, LPData, LJSONObject); end; function TNeonDeserializerJSON.ReadRecord(const AParam: TNeonDeserializerParam; const AData: TValue): TValue; var LJSONObject: TJSONObject; LPData: Pointer; begin Result := AData; LPData := AData.GetReferenceToRawData; if not Assigned(LPData) then Exit; // Objects, Records, Interfaces are all represented by JSON objects LJSONObject := AParam.JSONValue as TJSONObject; ReadMembers(AParam.RttiType, LPData, LJSONObject); end; function TNeonDeserializerJSON.ReadSet(const AParam: TNeonDeserializerParam): TValue; var LSetStr: string; begin LSetStr := AParam.JSONValue.Value; LSetStr := LSetStr.Replace(sLineBreak, '', [rfReplaceAll]); LSetStr := LSetStr.Replace(' ', '', [rfReplaceAll]); TValue.Make(StringToSet(AParam.RttiType.Handle, LSetStr), AParam.RttiType.Handle, Result); end; function TNeonDeserializerJSON.ReadStreamable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean; var LStream: TMemoryStream; LStreamable: IDynamicStream; LJSONValue: TJSONValue; begin Result := False; LStreamable := TDynamicStream.GuessType(AData.AsObject); if Assigned(LStreamable) then begin Result := True; LStream := TMemoryStream.Create; try if IsOriginalInstance(AData) then LJSONValue := (AParam.JSONValue as TJSONObject).GetValue('$value') else LJSONValue := AParam.JSONValue; TBase64.Decode(LJSONValue.Value, LStream); LStream.Position := soFromBeginning; LStreamable.LoadFromStream(LStream); finally LStream.Free; end; end; end; function TNeonDeserializerJSON.ReadString(const AParam: TNeonDeserializerParam): TValue; begin case AParam.RttiType.TypeKind of // AnsiString tkLString: Result := TValue.From(UTF8String(AParam.JSONValue.Value)); //WideString tkWString: Result := TValue.From(AParam.JSONValue.Value); //UnicodeString tkUString: Result := TValue.From(AParam.JSONValue.Value); //ShortString tkString: Result := TValue.From(UTF8String(AParam.JSONValue.Value)); // Future string types treated as unicode strings else Result := AParam.JSONValue.Value; end; end; function TNeonDeserializerJSON.ReadVariant(const AParam: TNeonDeserializerParam): TValue; var LDateTime: TDateTime; LJSONNumber: TJSONNumber; LJSONString: TJSONString; begin if AParam.JSONValue is TJSONNull then Result := TValue.FromVariant(Null) else if AParam.JSONValue is TJSONTrue then Result := TValue.FromVariant(True) else if AParam.JSONValue is TJSONFalse then Result := TValue.FromVariant(False) else if AParam.JSONValue is TJSONNumber then begin LJSONNumber := AParam.JSONValue as TJSONNumber; Result := TValue.FromVariant(LJSONNumber.AsDouble); end else if AParam.JSONValue is TJSONString then begin LJSONString := AParam.JSONValue as TJSONString; try LDateTime := ISO8601ToDate(LJSONString.Value, FConfig.UseUTCDate); Result := TValue.FromVariant(VarFromDateTime(LDateTime)) except Result := TValue.FromVariant(LJSONString.Value); end; end; end; function TNeonDeserializerJSON.JSONToArray(AJSON: TJSONValue; AType: TRttiType): TValue; begin Result := ReadDataMember(AJSON, AType, TValue.Empty); end; procedure TNeonDeserializerJSON.JSONToObject(AObject: TObject; AJSON: TJSONValue); var LType: TRttiType; LValue: TValue; begin FOriginalInstance := AObject; LType := TRttiUtils.Context.GetType(AObject.ClassType); LValue := AObject; ReadDataMember(AJSON, LType, AObject); end; function TNeonDeserializerJSON.JSONToTValue(AJSON: TJSONValue; AType: TRttiType; const AData: TValue): TValue; begin FOriginalInstance := AData; Result := ReadDataMember(AJSON, AType, AData); end; function TNeonDeserializerJSON.JSONToTValue(AJSON: TJSONValue; AType: TRttiType): TValue; begin //FOriginalInstance := TValue.Empty; Result := ReadDataMember(AJSON, AType, TValue.Empty); end; { TNeon } class function TNeon.JSONToObject(AType: TRttiType; AJSON: TJSONValue): TObject; begin Result := JSONToObject(AType, AJSON, TNeonConfiguration.Default); end; class function TNeon.JSONToObject(AType: TRttiType; const AJSON: string): TObject; begin Result := JSONToObject(AType, AJSON, TNeonConfiguration.Default); end; class function TNeon.JSONToObject(AType: TRttiType; AJSON: TJSONValue; AConfig: INeonConfiguration): TObject; begin Result := TRttiUtils.CreateInstance(AType); JSONToObject(Result, AJSON, AConfig); end; class function TNeon.JSONToObject(AJSON: TJSONValue): T; begin Result := JSONToObject(TRttiUtils.Context.GetType(TClass(T)), AJSON) as T; end; class procedure TNeon.JSONToObject(AObject: TObject; const AJSON: string; AConfig: INeonConfiguration); var LJSON: TJSONValue; begin LJSON := TJSONObject.ParseJSONValue(AJSON); try JSONToObject(AObject, LJSON, AConfig); finally LJSON.Free; end; end; class function TNeon.JSONToObject(const AJSON: string): T; begin Result := JSONToObject(TRttiUtils.Context.GetType(TClass(T)), AJSON) as T; end; class function TNeon.ObjectToJSON(AObject: TObject; AConfig: INeonConfiguration): TJSONValue; var LWriter: TNeonSerializerJSON; begin LWriter := TNeonSerializerJSON.Create(AConfig); try Result := LWriter.ObjectToJSON(AObject); finally LWriter.Free; end; end; class function TNeon.ObjectToJSONString(AObject: TObject): string; begin Result := TNeon.ObjectToJSONString(AObject, TNeonConfiguration.Default); end; class function TNeon.ObjectToJSON(AObject: TObject): TJSONValue; begin Result := TNeon.ObjectToJSON(AObject, TNeonConfiguration.Default); end; class function TNeon.ObjectToJSONString(AObject: TObject; AConfig: INeonConfiguration): string; var LJSON: TJSONValue; begin LJSON := ObjectToJSON(AObject, AConfig); try Result := Print(LJSON, AConfig.GetPrettyPrint); finally LJSON.Free; end; end; class procedure TNeon.ObjectToStream(AObject: TObject; AStream: TStream); begin ObjectToStream(AObject, AStream, TNeonConfiguration.Default); end; class procedure TNeon.ObjectToStream(AObject: TObject; AStream: TStream; AConfig: INeonConfiguration); var LJSON: TJSONValue; begin LJSON := TNeon.ObjectToJSON(AObject, AConfig); try PrintToStream(LJSON, AStream, AConfig.GetPrettyPrint); finally LJSON.Free; end; end; class function TNeon.Print(AJSONValue: TJSONValue; APretty: Boolean): string; var LWriter: TStringWriter; begin LWriter := TStringWriter.Create; try TNeon.PrintToWriter(AJSONValue, LWriter, APretty); Result := LWriter.ToString; finally LWriter.Free; end; end; class procedure TNeon.PrintToStream(AJSONValue: TJSONValue; AStream: TStream; APretty: Boolean); var LWriter: TStreamWriter; begin LWriter := TStreamWriter.Create(AStream); try TNeon.PrintToWriter(AJSONValue, LWriter, APretty); finally LWriter.Free; end; end; class procedure TNeon.PrintToWriter(AJSONValue: TJSONValue; AWriter: TTextWriter; APretty: Boolean); var LJSONString: string; LChar: Char; LOffset: Integer; LIndex: Integer; LOutsideString: Boolean; function Spaces(AOffset: Integer): string; begin Result := StringOfChar(#32, AOffset * 2); end; begin if not APretty then begin AWriter.Write(AJSONValue.ToJSON); Exit; end; LOffset := 0; LOutsideString := True; LJSONString := AJSONValue.ToJSON; for LIndex := 0 to Length(LJSONString) - 1 do begin LChar := LJSONString.Chars[LIndex]; if LChar = '"' then LOutsideString := not LOutsideString; if LOutsideString and (LChar = '{') then begin Inc(LOffset); AWriter.Write(LChar); AWriter.Write(sLineBreak); AWriter.Write(Spaces(LOffset)); end else if LOutsideString and (LChar = '}') then begin Dec(LOffset); AWriter.Write(sLineBreak); AWriter.Write(Spaces(LOffset)); AWriter.Write(LChar); end else if LOutsideString and (LChar = ',') then begin AWriter.Write(LChar); AWriter.Write(sLineBreak); AWriter.Write(Spaces(LOffset)); end else if LOutsideString and (LChar = '[') then begin Inc(LOffset); AWriter.Write(LChar); AWriter.Write(sLineBreak); AWriter.Write(Spaces(LOffset)); end else if LOutsideString and (LChar = ']') then begin Dec(LOffset); AWriter.Write(sLineBreak); AWriter.Write(Spaces(LOffset)); AWriter.Write(LChar); end else if LOutsideString and (LChar = ':') then begin AWriter.Write(LChar); AWriter.Write(' '); end else AWriter.Write(LChar); end; end; class function TNeon.ValueToJSON(const AValue: TValue): TJSONValue; begin Result := TNeon.ValueToJSON(AValue, TNeonConfiguration.Default); end; class function TNeon.ValueToJSON(const AValue: TValue; AConfig: INeonConfiguration): TJSONValue; var LWriter: TNeonSerializerJSON; begin LWriter := TNeonSerializerJSON.Create(AConfig); try Result := LWriter.ValueToJSON(AValue); finally LWriter.Free; end; end; class procedure TNeon.ValueToStream(const AValue: TValue; AStream: TStream); begin ValueToStream(AValue, AStream, TNeonConfiguration.Default); end; class procedure TNeon.ValueToStream(const AValue: TValue; AStream: TStream; AConfig: INeonConfiguration); var LJSON: TJSONValue; begin LJSON := TNeon.ValueToJSON(AValue, AConfig); try PrintToStream(LJSON, AStream, AConfig.GetPrettyPrint); finally LJSON.Free; end; end; class procedure TNeon.JSONToObject(AObject: TObject; AJSON: TJSONValue; AConfig: INeonConfiguration); var LReader: TNeonDeserializerJSON; begin LReader := TNeonDeserializerJSON.Create(AConfig); try LReader.JSONToObject(AObject, AJSON); finally LReader.Free; end; end; class function TNeon.JSONToObject(AType: TRttiType; const AJSON: string; AConfig: INeonConfiguration): TObject; var LJSON: TJSONValue; begin LJSON := TJSONObject.ParseJSONValue(AJSON); try Result := TRttiUtils.CreateInstance(AType); JSONToObject(Result, LJSON, AConfig); finally LJSON.Free; end; end; class function TNeon.JSONToObject(AJSON: TJSONValue; AConfig: INeonConfiguration): T; begin Result := JSONToObject(TRttiUtils.Context.GetType(TClass(T)), AJSON, AConfig) as T; end; class function TNeon.JSONToObject(const AJSON: string; AConfig: INeonConfiguration): T; begin Result := JSONToObject(TRttiUtils.Context.GetType(TClass(T)), AJSON, AConfig) as T; end; class function TNeon.JSONToValue(ARttiType: TRttiType; AJSON: TJSONValue; AConfig: INeonConfiguration): TValue; var LDes: TNeonDeserializerJSON; begin LDes := TNeonDeserializerJSON.Create(AConfig); try Result := LDes.JSONToTValue(AJSON, ARttiType); finally LDes.Free; end; end; class function TNeon.JSONToValue(ARttiType: TRttiType; AJSON: TJSONValue): TValue; begin Result := JSONToValue(ARttiType, AJSON, TNeonConfiguration.Default); end; class function TNeon.JSONToValue(AJSON: TJSONValue; AConfig: INeonConfiguration): T; var LDes: TNeonDeserializerJSON; LValue: TValue; LType: TRttiType; begin LDes := TNeonDeserializerJSON.Create(AConfig); try LType := TRttiUtils.Context.GetType(TypeInfo(T)); if not Assigned(LType) then raise ENeonException.Create('Empty RttiType in JSONToValue'); case LType.TypeKind of tkArray, tkRecord, tkDynArray: TValue.Make(nil, TypeInfo(T), LValue); else LValue := TValue.Empty; end; LValue := LDes.JSONToTValue(AJSON, LType, LValue); Result := LValue.AsType; finally LDes.Free; end; end; class function TNeon.JSONToValue(AJSON: TJSONValue): T; begin Result := JSONToValue(AJSON, TNeonConfiguration.Default); end; { TNeonDeserializerParam } procedure TNeonDeserializerParam.Default; begin JSONValue := nil; RttiType := nil; NeonObject := nil; end; end.