Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCustomSerializer for Simple Type (not for Class) #23

Closed
hafedh-trimeche opened this issue Sep 24, 2020 · 2 comments
Closed

TCustomSerializer for Simple Type (not for Class) #23

hafedh-trimeche opened this issue Sep 24, 2020 · 2 comments

Comments

@hafedh-trimeche
Copy link

hafedh-trimeche commented Sep 24, 2020

Please how to implement TCustomSerializer for TBytes?
The content of TBytes would be a container for a Binary Data, Encrepted Data, ... So marshaling TBytes is made by Base64 encoding/decoding.

  TBytesSerializer = class(TCustomSerializer)
  protected
    class function GetTargetInfo:PTypeInfo;override;
    class function CanHandle(AType:PTypeInfo):Boolean;override;
  public
    function Serialize(const AValue:TValue;ANeonObject:TNeonRttiObject;AContext:ISerializerContext):TJSONValue;override;
    function Deserialize(AValue:TJSONValue;const AData:TValue;ANeonObject:TNeonRttiObject;AContext:IDeserializerContext):TValue;override;
  end;

implementation

class function TBytesSerializer.GetTargetInfo: PTypeInfo;
begin
  Result := TypeInfo(TBytes);
end;

class function TBytesSerializer.CanHandle(AType: PTypeInfo): Boolean;
begin
  Result := (AType=GetTargetInfo);
end;

function TBytesSerializer.Serialize(const AValue:TValue;ANeonObject:TNeonRttiObject;AContext:ISerializerContext):TJSONValue;
var
  LBytes  : TBytes;
  LBase64 : string;
begin
  SetLength(LBytes,AValue.GetArrayLength);
  if Length(LBytes)>0 then 
  begin
    AValue.ExtractRawData(LBytes);
    LBase64 := TBase64.Encode(LBytes);
  end
  else LBase64 := '';
  Result := TJSONString.Create(LBase64);
end;

function TBytesSerializer.Deserialize(AValue:TJSONValue;const AData:TValue;ANeonObject:TNeonRttiObject;AContext:IDeserializerContext):TValue;
var
  LBytes : TBytes;
begin
  LBytes := TBase64.Decode(AValue.Value);
  AData.Make(LBytes,GetTargetInfo,Result);
end;

Best regards.

@JensMertelmeyer
Copy link
Contributor

JensMertelmeyer commented Sep 25, 2020

You're using TValue.Make and TValue.ExtractRawData. This basically marshals your array pointer, not the array content.

Your serializer methods can be much easier:

function TBytesSerializer.Serialize(
  const AValue: TValue;
  ANeonObject: TNeonRttiObject;
  AContext: ISerializerContext
): TJSONValue;
var
	LBytes:  TBytes;
	LBase64: string;
begin
	LBytes := AValue.AsType<TBytes>();
	LBase64 := TBase64.Encode(LBytes);
	Result := TJSONString.Create(LBase64);
end;

function TBytesSerializer.Deserialize(
  AValue: TJSONValue;
  const AData: TValue;
  ANeonObject: TNeonRttiObject;
  AContext: IDeserializerContext
): TValue;
var
	LBytes: TBytes;
begin
	LBytes := TBase64.Decode(AValue.Value);
	Result := TValue.From(LBytes);
end;

I have attached your modified unit and a few testcases here:
https://gist.github.com/JensMertelmeyer/db915827dd550f19e46cb3f6de09df1f

It shows it will also work with TBytes that represent things like a 'Hello World 😎'

PS: No testcases yet on how it handles invalid Base64 strings 😉

PPS: Neon handles TBytes just fine. Why are you implementing a custom Base64 encoder/decoder? To save space?

@hafedh-trimeche
Copy link
Author

Hi,
Thank you for these clarifications.
Indeed, when a TBytes field holds a large binary content, this content would be compressed and Base64 encoded.
I think that parsing Base64 is more performant than decoding array (TBytes) representation byte by byte.
Result (TBytes) := Decompress( Base64Decode(JsonBase64Content) ).

Best regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants