From deba0b2a29c814c06393218121ee975d0cf568db Mon Sep 17 00:00:00 2001 From: Paolo Rossi Date: Thu, 2 May 2024 09:59:28 +0200 Subject: [PATCH] Now you can pass a string to the NeonFormatAttribute so the format can be extensible Use of the NeonFormat enum is now deprecated --- Source/Neon.Core.Attributes.pas | 19 +++++++++-- Source/Neon.Core.Serializers.RTL.pas | 50 +++++++++++++++++++--------- Tests/Source/Neon.Tests.Entities.pas | 2 +- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/Source/Neon.Core.Attributes.pas b/Source/Neon.Core.Attributes.pas index cb6f83f..387b5b0 100644 --- a/Source/Neon.Core.Attributes.pas +++ b/Source/Neon.Core.Attributes.pas @@ -138,11 +138,13 @@ NeonIncludeAttribute = class(TCustomAttribute) /// Currently Base64 is supported for TBytes through a CustomSerializer /// NeonFormat = (Native, Base64); - NeonFormatAttribute = class(NeonAttribute) + NeonFormatAttribute = class(NeonNamedAttribute) private FFormatValue: NeonFormat; public - constructor Create(AOutputValue: NeonFormat = NeonFormat.Native); + constructor Create(AOutputValue: NeonFormat = NeonFormat.Native); overload; deprecated; + constructor Create(AOutputValue: string = 'native'); overload; + function IsValue(const AValue: string): Boolean; property FormatValue: NeonFormat read FFormatValue write FFormatValue; end; @@ -291,7 +293,7 @@ NeonAnyGetterAttribute = class(NeonAttribute); implementation uses - System.StrUtils, System.DateUtils; + System.StrUtils, System.TypInfo, System.DateUtils; { NeonNamedAttribute } @@ -345,6 +347,17 @@ constructor NeonEnumNamesAttribute.Create(const ANames: string); constructor NeonFormatAttribute.Create(AOutputValue: NeonFormat); begin FFormatValue := AOutputValue; + FValue := LowerCase(GetEnumName(TypeInfo(NeonFormat), Integer(AOutputValue))); +end; + +constructor NeonFormatAttribute.Create(AOutputValue: string); +begin + FValue := AOutputValue; +end; + +function NeonFormatAttribute.IsValue(const AValue: string): Boolean; +begin + Result := SameText(FValue, AValue); end; { NeonItemFactoryAttribute } diff --git a/Source/Neon.Core.Serializers.RTL.pas b/Source/Neon.Core.Serializers.RTL.pas index d05d1cc..a805450 100644 --- a/Source/Neon.Core.Serializers.RTL.pas +++ b/Source/Neon.Core.Serializers.RTL.pas @@ -88,6 +88,10 @@ TTValueSerializer = class(TCustomSerializer) /// Base64 using NeonFormat attribute /// TBytesSerializer = class(TCustomSerializer) + private + function IsFormatValue(AFormat: NeonFormatAttribute; const AValue: string): Boolean; inline; + + function ValueAsBase64(const AValue: TJSONValue): TValue; inline; protected class function GetTargetInfo: PTypeInfo; override; class function CanHandle(AType: PTypeInfo): Boolean; override; @@ -332,25 +336,26 @@ class function TBytesSerializer.CanHandle(AType: PTypeInfo): Boolean; function TBytesSerializer.Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; var - LVal: TBytes; LType: TRttiType; LFormat: NeonFormatAttribute; begin LFormat := ANeonObject.GetAttribute; - if Assigned(LFormat) and (LFormat.FormatValue = NeonFormat.Native) then + if IsFormatValue(LFormat, 'native') then begin LType := TRttiUtils.Context.GetType(TypeInfo(TBytes)); - Result := AContext.ReadDataMember(AValue, LType, AData, False); - end - else - begin - if (AValue is TJSONString) then - LVal := TBase64.Decode(AValue.Value) - else - raise ENeonException.Create('JSONValue must be a string'); - - Result := TValue.From(LVal); + Exit(AContext.ReadDataMember(AValue, LType, AData, False)); end; + + //if IsFormatValue(LFormat, 'base64') then + Result := ValueAsBase64(AValue); +end; + +function TBytesSerializer.IsFormatValue(AFormat: NeonFormatAttribute; const AValue: string): Boolean; +begin + if not Assigned(AFormat) then + Exit(False); + + Result := AFormat.IsValue(AValue); end; class function TBytesSerializer.GetTargetInfo: PTypeInfo; @@ -366,10 +371,23 @@ function TBytesSerializer.Serialize(const AValue: TValue; begin LVal := AValue.AsType; LFormat := ANeonObject.GetAttribute; - if Assigned(LFormat) and (LFormat.FormatValue = NeonFormat.Native) then - Result := AContext.WriteDataMember(AValue, False) - else - Result := TJSONString.Create(TBase64.Encode(LVal)); + + if IsFormatValue(LFormat, 'native') then + Exit(AContext.WriteDataMember(AValue, False)); + + //if IsFormatValue(LFormat, 'base64') then + Exit(TJSONString.Create(TBase64.Encode(LVal))); +end; + +function TBytesSerializer.ValueAsBase64(const AValue: TJSONValue): TValue; +var + LVal: TBytes; +begin + if not (AValue is TJSONString) then + raise ENeonException.Create('JSONValue must be a string'); + + LVal := TBase64.Decode(AValue.Value); + Result := TValue.From(LVal); end; end. diff --git a/Tests/Source/Neon.Tests.Entities.pas b/Tests/Source/Neon.Tests.Entities.pas index 61eac9c..538c1f9 100644 --- a/Tests/Source/Neon.Tests.Entities.pas +++ b/Tests/Source/Neon.Tests.Entities.pas @@ -45,7 +45,7 @@ interface TBytes64Rec = record Bytes: TBytes; - [NeonFormat(NeonFormat.Native)] + [NeonFormat('native')] ByteArray: TBytes; end;