GithubHelp home page GithubHelp logo

delphi-neon's Introduction

Hi there 👋 these are my stats:

My GitHub stats

Top Languages

delphi-neon's People

Contributors

carlobarazzetta avatar ccy avatar davidevisconti avatar dominikcz avatar fatih-duman avatar ganacereddine avatar gcarneiroa avatar gliden avatar holgerflick avatar jensmertelmeyer avatar mattiavicari avatar paolo-rossi avatar patrickbs96 avatar paustr avatar rjantz2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

delphi-neon's Issues

Return null instead of empty in Tdatatime

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;

if AValue.AsType <> 0 then
Result := TJSONString.Create(TJSONUtils.DateToJSON(AValue.AsType, FConfig.UseUTCDate))
else
Result := TJSONNull.Create;

end;

Unintentional free of property in Neon.Core.Persistence.JSON

I've been having a problem with deserializing a class I have. It seems to destroy the property when it should not.

The code is in line 1247 (method TNeonDeserializerJSON.ReadMembers) and the 2 lines are

    LMemberValue := ReadDataMember(LParam, LNeonMember.GetValue);
    LNeonMember.SetValue(LMemberValue);

if I have a class with a property like

property MyProp: TObject read GetMyProp write SetMyProp;

and the SetMyProp looks like this

Procedure SomeObject.SetMyProp(Value: TObject);
begin
if Assigned(FMyProp) then
FMyProp.Free;
FMyProp := Value;
end;

then the 2 mentioned lines perform as follows.

The 1st line gets a copy of the object into LMemberValue
The second line then tries to set the value to the same value, but this ends up disposing of the value.

I suspect this is possibly a technicalality of the code and may not really be a bug, but might be something that cannot easily be remedied. Perhaps a new attribute could solve it. In the mean time I'll try to disable my free in the property setter until after the deserializing has completed.

Another solution would be not to free it in the setter but have the caller free it before setting it, but that doesn't quite feel right.

Any thoughts ?

Add ValueToJSONString (or similar) to JSON persistence

Unless I've missed it, it would be nice for TNeon to have a ValueToJSONString method. Currently I'm using this helper method:

class function TNeonHelper.ValueToJSONString(const AValue: TValue): string;
var
  LJSON: TJSONValue;
begin
  LJSON := ValueToJSON(AValue);
  try
    Result := LJSON.ToString;
  finally
    LJSON.Free;
  end;
end;

Saves having to manage the TJSONValue yourself.. and the reverse:

class function TNeonHelper.JSONStringToValue<T>(const AJSON: string): T;
begin
  Result := JSONStringToValue<T>(AJSON, nil);
end;

class function TNeonHelper.JSONStringToValue<T>(const AJSON: string; const AConfig: INeonConfiguration): T;
var
  LJSON: TJSONValue;
begin
  LJSON := TJSONObject.ParseJSONValue(AJSON);
  if LJSON = nil then
    LJSON := TJSONObject.Create;
  try
    if AConfig = nil then
      Result := JSONToValue<T>(LJSON)
    else
      Result := JSONToValue<T>(LJSON, AConfig);
  finally
    LJSON.Free;
  end;
end;

Deserialize sub-objects which are not created in constructor

Hi,
Our team wants to use Neon library for complex type with sub-objects. When the sub-objects are created in constructor everything works prefectly, but we want to have them unassigned unless they are provided in json.
For example :
We have a class

TPerson = class
  private
    Fname: string;
    FSurname: string;
    FNote: TNote;
  published
    property Name: string read Fname write Fname;

    [NeonProperty('LastName')]
    property Surname: string read FSurname write FSurname;
    property Note: TNote read FNote write FNote;
  end;

When we want to deserialize json:

  {
    "Name":"Paolo",
    "LastName":"Rossi",
    "Note":{
        "Text":"ADFGH"
    }
}

the Note property is skipped and remains nil.

Is it possible to assign the Note property without creating it first in TPerson's constructor?

Unwanted behavior when deserializing empty strings or enums

Hello Paolo
I have updated Delphi-Neon recently and I found different behavior comparing to the version before. I use deserialization from JSON and classes with Nullable<> types.

In short, the problem is with deserializing an empty string from JSON to Nullable<> type. It is deserialized with flag HasValue = false, but should be true.

Here's a simple example that shows the behavior and what's wrong with that. There are 4 test methods. Look for '//FAIL' comment in runTest2 below:

program Example1Proj;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections,
  System.TypInfo,
  System.Json,
  Neon.Core.Types,
  Neon.Core.Attributes,
  Neon.Core.Nullables,
  Neon.Core.Persistence,
  Neon.Core.Persistence.JSON;

type
  TExampleEnumNames = class
  public const
    cEMPTY = '';
    cENUM1 = 'ENUM1';
    EnumNames = cEMPTY + ',' + cENUM1;
  end;

  [NeonEnumNames(TExampleEnumNames.Enumnames)]
  TExampleEnum = (
    evEMPTY,
    evENUM1
  );

  TExampleClass = class
  private
    FExampleEnum: Nullable<TExampleEnum>;
    FExampleString: Nullable<string>;

  public

    [NeonInclude(IncludeIf.NotNull)]
    property exampleEnum: Nullable<TExampleEnum> read FExampleEnum write FExampleEnum;
    [NeonInclude(IncludeIf.NotNull)]
    property exampleString: Nullable<string> read FExampleString write FExampleString;
  end;


function getNeonConfig: INeonConfiguration;
begin
  result := TNeonConfiguration.Default;
  result.SetMemberCase(TNeonCase.Unchanged);
  result.SetMembers([TNeonMembers.Fields, TNeonMembers.Properties]);
  result.SetIgnoreFieldPrefix(True);
  result.SetVisibility([mvPublic, mvPublished]);
  result.SetUseUTCDate(false);
  result.SetEnumAsInt(false);
end;


procedure DeserializeObject(AObject: TObject; const AJson: string);
var
  LJSON: TJSONValue;
  lConfig: INeonConfiguration;
  LReader: TNeonDeserializerJSON;
begin
  lConfig := getNeonConfig;

  LJSON := TJSONObject.ParseJSONValue(AJson);
  if not Assigned(LJSON) then
    raise ENeonException.Create('Unable to parse json');
  try
    LReader := TNeonDeserializerJSON.Create(lConfig);
    try
      LReader.JSONToObject(AObject, LJSON);
      if LReader.Errors.Count > 0 then
        raise ENeonException.Create(LReader.Errors.Text);
    finally
      LReader.Free;
    end;
  finally
    LJSON.Free;
  end;
end;

procedure runTest1;
var
  testObj: TExampleClass;
begin
  testObj := TExampleClass.Create;
  DeserializeObject(testObj, '{ "exampleString": "ABC", "exampleEnum": "ENUM1" }');

  assert(testObj <> nil); //OK
  assert(testObj.exampleString.HasValue = true); //OK
  assert(testObj.exampleString.Equals('ABC')); //OK

  assert(testObj.exampleEnum.HasValue = true); //OK
  assert(testObj.exampleEnum = evENUM1); //OK
end;

procedure runTest2;
var
  testObj: TExampleClass;
begin
  testObj := TExampleClass.Create;
  DeserializeObject(testObj, '{ "exampleString": "", "exampleEnum": "" }');

  { Below there are two fails that I consider as invalid behavior }
  assert(testObj <> nil); //OK
  assert(testObj.exampleString.HasValue = true); //FAIL
  assert(testObj.exampleString.Equals('')); //OK

  assert(testObj.exampleEnum.HasValue = true); //FAIL
  assert(testObj.exampleEnum = evEMPTY); //OK
end;

procedure runTest3;
var
  testObj: TExampleClass;
  z: string;
begin
  testObj := TExampleClass.Create;
  DeserializeObject(testObj, '{ "exampleString": null, "exampleEnum": null }');

  assert(testObj <> nil); //OK
  assert(testObj.exampleString.HasValue = false); //OK
  //assert(testObj.exampleString.Equals('')); //OK - can't test this; it has no value

  assert(testObj.exampleEnum.HasValue = false); //OK
  //assert(testObj.exampleEnum = evEMPTY); //OK - cant't test this; it has no value
end;

procedure runTest4;
var
  testObj: TExampleClass;
begin
  testObj := TExampleClass.Create;
  DeserializeObject(testObj, '{}');

  assert(testObj <> nil); //OK
  assert(testObj.exampleString.HasValue = false); //OK
  //assert(testObj.exampleString.Equals('')); //OK - can't test this; it has no value

  assert(testObj.exampleEnum.HasValue = false); //OK
  //assert(testObj.exampleEnum = evEMPTY); //OK - cant't test this; it has no value
end;

begin
  try
    runTest1;
    runTest2;
    runTest3;
    runTest4;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

In my opinion it works wrong because of the method in Neon.Core.Utils.pas:

      class function TJSONUtils.IsNotEmpty(const AJSON: TJSONValue): Boolean;

There wasn't that method in previous version of Neon and it worked as expected. Now I have this issue. When the string is set and empty in JSON, it should be deserialized as: Value = '' and HasValue = 'True'

So I think that these 2 lines in mentioned method should be removed:

    if AJSON is TJSONString then
      Exit(not (AJSON as TJSONString).Value.IsEmpty);`

When the value is regarded as empty, there is no attempt to deserialize it and HasValue is not set.

Thank you

Nullable<Enum> - Not using custom names

I have tested in my application and with your application to use a Nullable value. Your example is the TClassOfNullables with Nullable

It is not using the NeonEnumNames when used in this manner Nullable

Regards,
Jacques

suggestion: Record serializer

This Serializer would be used for Records.

type
  Serializer =
  record
    class function Marshal<T>(const Data:T):string;inline;static;
    class function Unmarshal<T>(const JsonText:string;out Data:T):Boolean;inline;static;
  end;

implementation

class function Serializer.Marshal<T>(const Data:T):string;
begin
  try
    Result := TNeon.ValueToJSON(TValue.From(Data)).ToJSON;
  except
    Result := '{null}';
  end;
end;

class function Serializer.Unmarshal<T>(const JsonText:string;out Data:T):Boolean;
var
  AJSON : TJSONObject;
begin
  AJSON  := TJSONObject.Create;
  Result := False;
  try
    AJSON.Parse(BytesOf(JsonText),0);
    Data   := TNeon.JSONToValue<T>(AJSON);
    Result := True;
  except
    Data := default(T);
  end;
  FreeAndNil(AJSON);
end;

Best regards.

Exception when reading dynamic array of arrays

Using the following code:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure DynArrayTest;
    procedure DynGridTest;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  System.IOUtils, System.Rtti, System.JSON,
  Neon.Core.Persistence, Neon.Core.Persistence.JSON;

type
  TNeonHelper = class helper for TNeon
  public
    class function JSONStringToValue<T>(const AJSON: string): T; overload;
    class function JSONStringToValue<T>(const AJSON: string; const AConfig: INeonConfiguration): T; overload;
  end;

  TRow = TArray<Int64>;

  TGrid = TArray<TRow>;

  TTest = record
    Grid: TGrid;
    constructor Create(const AWidth, AHeight: Integer);
  end;

  TTest2 = record
    Row: TRow;
    constructor Create(const AWidth: Integer);
  end;

class function TNeonHelper.JSONStringToValue<T>(const AJSON: string): T;
begin
  Result := JSONStringToValue<T>(AJSON, nil);
end;

class function TNeonHelper.JSONStringToValue<T>(const AJSON: string; const AConfig: INeonConfiguration): T;
var
  LJSON: TJSONValue;
begin
  LJSON := TJSONObject.ParseJSONValue(AJSON);
  if LJSON = nil then
    LJSON := TJSONObject.Create;
  try
    if AConfig = nil then
      Result := JSONToValue<T>(LJSON)
    else
      Result := JSONToValue<T>(LJSON, AConfig);
  finally
    LJSON.Free;
  end;
end;

{ TTest }

constructor TTest.Create(const AWidth, AHeight: Integer);
var
  I: Integer;
begin
  SetLength(Grid, AHeight);
  for I := 0 to AHeight - 1 do
    SetLength(Grid[I], AWidth);
end;

{ TTest2 }

constructor TTest2.Create(const AWidth: Integer);
begin
  SetLength(Row, AWidth);
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  // DynArrayTest;
  DynGridTest;
end;

procedure TForm1.DynArrayTest;
var
  LTest2: TTest2;
  LFileName: string;
begin
  LFileName := TPath.Combine(TPath.GetTempPath, 'test2.json');
  LTest2 := TTest2.Create(5);
  TFile.WriteAllText(LFileName, TNeon.ValueToJSONString(TValue.From(LTest2)));
  LTest2 := TNeon.JSONStringToValue<TTest2>(TFile.ReadAllText(LFileName));
end;

procedure TForm1.DynGridTest;
var
  LTest: TTest;
  LFileName: string;
begin
  LFileName := TPath.Combine(TPath.GetTempPath, 'test.json');
  LTest := TTest.Create(5, 7);
  TFile.WriteAllText(LFileName, TNeon.ValueToJSONString(TValue.From(LTest)));
  LTest := TNeon.JSONStringToValue<TTest>(TFile.ReadAllText(LFileName));
end;

end.

The DynGridTest method manages to serialize the JSON for LTest, however it fails to deserialize. The root cause is the TRttiUtils.CreateNewValue method in Neon.Core.Utils which does not handle tkDynArray, thus raises an exception.

Delphi 2010

In my opinion Delphi 2010 is too old :(

In your code I see "string.split" or "string.isEmpty" from System.SysUtils.TStringHelper

ReadEnum should use TJSONBool

TNeonDeserializerJSON.ReadEnum should use TJSONBool instead of TJSONTrue and TJSONFalse.

type
  TClassWithBool = class
  private
    FBoolProp: Boolean;
  public
    property BoolProp: Boolean read FBoolProp write FBoolProp;
  end;

procedure TestBool;
var
  LJSON: TJSONObject;
  LReader: TNeonDeserializerJSON;
  LClass: TClassWithBool;
begin
  LClass := nil;
  LReader := nil;
  LJSON := TJSONObject.Create;
  try
    LJSON.AddPair('BoolProp', True);
    LReader := TNeonDeserializerJSON.Create(TNeonConfiguration.Default);
    LClass := TClassWithBool.Create;
    LReader.JSONToObject(LClass, LJSON);
    ShowMessage(LReader.Errors.Text); // => Error converting member [BoolProp] of type [TClassWithBool]: Invalid JSON value. Boolean expected
  finally
    LJSON.Free;
    LClass.Free;
    LReader.Free;
  end;
end;

Suggested changes

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 TJSONBool then
      Result := (AParam.JSONValue as TJSONBool).AsBoolean
    else
      raise ENeonException.Create('Invalid JSON value. Boolean expected');
  end

[...]

request

Can I set the number of decimal places for persistence?

Deserialising GUID to TGUID exception: "EInvalidCast"

I can reproduce this exception with the newest version of neon-json with the code below.

program NeonBugTGUID;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils,system.json,
    Neon.Core.Types,
    Neon.Core.Attributes,
    Neon.Core.Persistence,
    Neon.Core.Persistence.JSON,
    Neon.Core.Utils;

type
  TMyData = class
  private
  public
    ID: TGUID;
end;

var
  JSONString: string;
  MyData: TMyData;
  JSONValue: TJSONValue;
  config: INeonConfiguration;
begin
  config:=TNeonConfiguration.Default;
  config.SetMembers([TNeonMembers.Fields]);
  try
    JSONString := '{"ID": "B7A1A8D1-82D3-4D02-99C7-6A538B066FC4"}';
    MyData := TMyData.Create;
    JSONValue := TJSONObject.ParseJSONValue(JSONString);

    try
      TNeon.JSONToObject(MyData, JSONValue, config);
      WriteLn('GUID aus JSON gelesen: ', GUIDToString(MyData.ID));
    finally
      JSONValue.Free;
    end;

    ReadLn;
  finally
    MyData.Free;
  end;
end.

Error deserializing nested array json

I am trying to deserialize the following json below, and the component is causing an error in the method.

function TNeonDeserializerJSON.ReadEnumerable(const AParam: TNeonDeserializerParam; const AData: TValue): Boolean;

This is because I have an array within an array, and the elements of the last array are merged between integers and string.
When the system tries to call ReadDataMember again passing LItemValue as an integer, it does not recognize and gives an error.
Would you help me ?

{ "iTotalRecords": 34, "iTotalDisplayRecords": 34, "aaData": [ [ 1, 1, 1, "Papel de 5 cm", "Sim", "Não", "Não", " " ], [ 2, 1, 1, "Papel de 6 cm", "Sim", "Não", "Não", " " ], [ 3, 1, 1, "Papel de 7 cm", "Sim", "Não", "Não", " " ], [ 4, 1, 1, "Papel de 8 cm", "Sim", "Não", "Não", " " ], [ 5, 1, 1, "Papel de 9 cm", "Sim", "Não", "Não", " " ], [ 6, 1, 1, "Papel de 10 cm", "Sim", "Não", "Não", " " ], [ 7, 1, 1, "Papel de 11 cm", "Sim", "Não", "Não", " " ], [ 8, 1, 1, "Papel de 12 cm", "Sim", "Não", "Não", " " ], [ 9, 1, 1, "Papel de 15 cm", "Sim", "Não", "Não", " " ], [ 10, 1, 1, "Papel de 17 cm", "Sim", "Não", "Não", " " ], [ 11, 1, 1, "Papel de 20 cm", "Sim", "Não", "Não", " " ], [ 12, 1, 1, "Papel de 25 cm", "Sim", "Não", "Não", " " ], [ 13, 1, 1, "Papel de 30 cm", "Sim", "Não", "Não", " " ], [ 14, 1, 1, "Papel de 35 cm", "Sim", "Não", "Não", " " ], [ 15, 1, 1, "Papel de 40 cm", "Sim", "Não", "Não", " " ], [ 16, 1, 1, "Papel de 45 cm", "Sim", "Não", "Não", " " ], [ 17, 1, 1, "Papel de 50 cm", "Sim", "Não", "Não", " " ], [ 18, 1, 1, "Papel de 60 cm", "Sim", "Não", "Não", " " ], [ 19, 1, 1, "Papel de 13 cm", "Sim", "Não", "Não", " " ], [ 20, 1, 1, "Envelope 12 x 10", "Sim", "Não", "Não", " " ], [ 21, 1, 1, "Envelope 10 x 18", "Sim", "Não", "Não", " " ], [ 22, 1, 1, "Envelope 12 x 15", "Sim", "Não", "Não", " " ], [ 23, 1, 1, "Envelope 12 x 28", "Sim", "Não", "Não", " " ], [ 24, 1, 1, "Envelope 15 x 17", "Sim", "Não", "Não", " " ], [ 25, 1, 1, "Envelope 15 x 29", "Sim", "Não", "Não", " " ], [ 26, 1, 1, "Envelope 20 x 24", "Sim", "Não", "Não", " " ], [ 27, 1, 1, "Envelope 20 x 40", "Sim", "Não", "Não", " " ], [ 28, 1, 1, "Envelope 25 x 27", "Sim", "Não", "Não", " " ], [ 29, 1, 1, "Envelope 25 x 35", "Sim", "Não", "Não", " " ], [ 30, 1, 1, "Envelope 30 x 36", "Sim", "Não", "Não", " " ], [ 32, 1, 1, "SMS PESADO 1200X1200", "Sim", "Não", "Não", " " ], [ 34, 1, 1, "PAPEL GRAU CIRURGICO", "Sim", "Sim", "Sim", " " ], [ 35, 1, 1, "SMS", "Sim", "Sim", "Não", " " ], [ 36, 1, 1, "Tecido", "Sim", "Sim", "Não", " " ] ] }

TCustomSerializer for Simple Type (not for Class)

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.

TTime Serialization

The TTime is serialized as TDateTime.

12:15:00 is serialized as 1899-12-30T12:15:00.000Z instead of 12:15:00.

Best regards.

error generating swagger json

I have a problem converting this structure into swagger.json.
If I am calling the /swagger endpoint from WiRL the service crashes.
If I call the GET endpoint that returns this structure then it works fine.

TObjectA = class;

TObjectA = class 
private 
   FNodes: TObjectDictionary<string, TObjectA>
public
   property Nodes : TObjectDictionary<string, TObjectA> read FNodes write FNodes;
end;

How can I fix this problem?

Thanks

Set of Enum

I am trying to use a set of ENUM e.g.

[NeonEnumNames('Low Speed,Medium Speed,High Speed')]
TEnumSpeed = (Low, Medium, High);

TEnumSpeedSet = set of TEnumSpeed

In this use case it does not use the NeonEnumNames.

I have then tried to change it to an array:

TEnumSpeedSet = Array of TEnumSpeed

It then creates it correctly, but when serializing back to object it fails?

Any suggestion

Memory leak on Nullable<class>

Hi, Great library. Does way more than all the others !

If I create a property like
property Prop: Nullable<TObject> read FProp write FProp;
and in the constructor have
FProp := TObject.Create;
if I assign nil to the property in my code like

var x: TMyData;
x := TMyData.Create; // constructor creates instance of TObject for FProp
x.Prop := nil;

x.Prop is not freed. I could free it manually, but in the TNeon.JSONToObject I don't get that opportunity and the object leaks. eg, Json string is
{}
ie there is no Prop in the JSON string. This creates the object correctly but rather than having a nil value for Prop, it has a TObject there and the HasValue property for Prop is true even though it had no value in the JSON.

I looked at the available attributes and couldn't find one that would solve this. Is this a bug or have I done something wrong ?

I think I can get around it by processing the JSON objects and removing any TJsonClass entries that have nothing in them.

MORE DETAIL

It seems the constructor works as expected, so no problem there. It also seems the correct way to destroy a Nullable<TObject> in the destructor is
if FProp.HasValue then
TObject(FProp).Free;

and to assign a nil to the property
TObject(x.Prop).Free;
x.Prop := nil;

and to assign a new non-nil object

TObject(x.Prop).Free;
x.Prop := TObject.Create;

If I make sure I do all this, then no leaks occur.

Sub-Type & Object serialization

Hello,

When a record embeds an object, the Serialization is done but Deserialization omits the object (would be recreated).

When a Sub-Type of TDateTime is used (TCustomDatetime = type TDateTime), it is serialized to Float.

type
  TSubDateTime = type TDateTime;

  TPersonObject=class
  private
    FP1 : Integer;
    FP2 : TBytes;
    FP3 : string;
  public
    property P1 : Integer read FP1 write FP1;
    property P2 : TBytes  read FP2 write FP2;
    property P3 : string  read FP3 write FP3;
  end;

  TPersonData=
  record
    First ,
    Last  : string;
    Birth : TSubDateTime;
    O     : TPersonObject;
  end;
procedure TForm1.SubTypeBtnClick(Sender: TObject);
var
  LObject : TPersonObject;
  Data    : TPersonData;
  LValue  : TValue;
  LJSON   : TJSONValue;
  sJSON   : string;
begin
  Data   := default(TPersonData);
  Data.O := TPersonObject.Create;
  with Data.O do
  begin
    P1 := 11;
    P2 := [65,66,67];
    P3 := 'Object';
  end;
  with Data do
  begin
    First := 'First';
    Last  := 'Last';
    Birth := Now;
  end;
  LValue           := TValue.From(Data);
  LJSON            := TNeon.ValueToJSON(LValue);
  sJSON            := LJSON.ToJSON;
  Memo1.Lines.Text := '----- Serialize -----';
  Memo1.Lines.Add(sJSON);
  FreeAndNil(LJSON);
  FreeAndNil(Data.O);
  /////////////////////////////////////////////////////////
  LJSON  := TJSONObject.ParseJSONValue(sJSON);
  Data   := TNeon.JSONToValue<TPersonData>(LJSON);
  FreeAndNil(LJSON);
  /////////////////////////////////////////////////////////
  LValue := TValue.From(Data);
  LJSON  := TNeon.ValueToJSON(LValue);
  sJSON  := LJSON.ToJSON;
  FreeAndNil(LJSON);
  Memo1.Lines.Add('----- Deserialize/Serialize -----');
  Memo1.Lines.Add(sJSON);
  if Assigned(Data.O) then FreeAndNil(Data.O);
end;

Best regards.

Problem on deserialize objectLists inside objectLists

Hi i'm having problem deserializing a complex class, i already seen the complex deserialization on the demos, but my class is little bit more complex, i have ObjectLists inside ObjectLists.
I don't know if i'm doing something wrong when declaring those models, but the property Numeros which is a TObjectList<TNumero> inside the property Enderecos it's not being deserialized.

Here's my model classes(Getters and Setters implementation omitted for code clarity) :

TCliente :

type
  TCliente = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override;
  published
    property idCliente: integer read FidCliente write SetidCliente;
    property NomeFantasia: string read FNomeFantasia write SetNomeFantasia;
    property Enderecos: TObjectList<TEndereco> read FEnderecos write SetEnderecos;
    property NovoCampoString: string read FNovoCampoString write SetNovoCampoString;
  end;

implementation

{ TCliente }

constructor TCliente.Create(AOwner: TComponent);
begin
  inherited;
  Enderecos := TObjectList<TEndereco>.Create;
end;

TEndereco:

type
  TEndereco = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override;
  published
    property idCliente: integer read FidCliente write SetidCliente;
    property idEndereco: integer read FidEndereco write SetidEndereco;
    property Logradouro: string read FLogradouro write SetLogradouro;
    property Bairro: string read FBairro write SetBairro;
    property Cidade: string read FCidade write SetCidade;
    property Numeros: TObjectList<TNumero> read FNumeros write SetNumeros;
  end;

implementation

{ TEndereco }

constructor TEndereco.Create(AOwner: TComponent);
begin
  inherited;
  Numeros := TObjectList<TNumero>.Create(True);
end;

And TNumero:

type
  TNumero = class(TComponent)
  published
    property Numero: string read FNumero write SetNumero;
    property Cor: string read FCor write SetCor;
  end;

implementation

When i manually populate the instance of TCliente class, serialization goes perfectly.
The Neon library serialize the following code :

Cliente := TCliente.Create(self);
Cliente.NomeFantasia := 'Alan';
Cliente.idCliente := 10;
Cliente.Senha := '1234';

Endereco := TEndereco.Create(self);
Endereco.idCliente := 10;
Endereco.idEndereco := 1;
Endereco.Logradouro := 'Rua salvador Simões';
Endereco.Bairro := 'Ipiranga';
Endereco.Cidade := 'São Paulo';

Numero := TNumero.Create(self);
Numero.Numero := '801';
Numero.Cor := 'Preto';
Endereco.numeros.Add(Numero);

Numero := TNumero.Create(self);
Numero.Numero := '111';
Numero.Cor := 'Amarelo';
Endereco.numeros.Add(Numero);

Cliente.Enderecos.Add(Endereco);

Endereco := TEndereco.Create(self);
Endereco.idCliente := 10;
Endereco.idEndereco := 2;
Endereco.Logradouro := 'Avenida Senador Vergueiro';
Endereco.Bairro := 'Jardim Hollywood';
Endereco.Cidade := 'São Bernardo do Campo';

Cliente.Enderecos.Add(Endereco);
LJSON := LWriter.ObjectToJSON(Cliente);

into this json :

{
    "idCliente": 10,
    "NomeFantasia": "Alan",
    "Enderecos": [
        {
            "idCliente": 10,
            "idEndereco": 1,
            "Logradouro": "Rua salvador Simões",
            "Bairro": "Ipiranga",
            "Cidade": "São Paulo",
            "Numeros": [
                {
                    "Numero": "801",
                    "Cor": "Preto"
                },
                {
                    "Numero": "111",
                    "Cor": "Amarelo"
                }
            ]
        },
        {
            "idCliente": 10,
            "idEndereco": 2,
            "Logradouro": "Avenida Senador Vergueiro",
            "Bairro": "Jardim Hollywood",
            "Cidade": "São Bernardo do Campo",
            "Numeros": []
        }
    ],
    "NovoCampoString": ""
}

But when deserialize this exactly same json into a TCliente object using this code :

var
  LJSON: TJSONValue;
  LReader: TNeonDeserializerJSON;
  LConfig: INeonConfiguration;
  Cliente: TCliente;
  LWriter: TNeonSerializerJSON;
begin
  memoError.Lines.Clear;
  mmoDeSerializar.Lines.Clear;

  LJSON := TJSONObject.ParseJSONValue(Memo1.Lines.Text); // < same json generated by the serialization

  Cliente := TCliente.Create(self);
  try
    LConfig := TNeonConfiguration.Default.SetVisibility([mvPublished]).SetPrettyPrint(True);
    LReader := TNeonDeserializerJSON.Create(LConfig);
    try
      LReader.JSONToObject(Cliente, LJSON);
      memoError.Lines.AddStrings(LReader.Errors);

      LWriter := TNeonSerializerJSON.Create(LConfig);
      mmoDeSerializar.Lines.Add(LWriter.ObjectToJSON(Cliente).ToString);
    finally
      LReader.Free;
    end;
  finally
    Cliente.Free;
    LJSON.Free;
  end;
end;

The Neon library don't deserialize the "Numeros" object, just the "Enderecos"
Here is the result:

{
    "idCliente": 10,
    "NomeFantasia": "",
    "Enderecos": [
        {
            "idCliente": 10,
            "idEndereco": 1,
            "Logradouro": "Rua salvador Simões",
            "Bairro": "Ipiranga",
            "Cidade": "São Paulo"
        },
        {
            "idCliente": 10,
            "idEndereco": 2,
            "Logradouro": "Avenida Senador Vergueiro",
            "Bairro": "Jardim Hollywood",
            "Cidade": "São Bernardo do Campo"
        }
    ],
    "NovoCampoString": ""
}

Please, can you help-me find what's wrong ?

Thank you!

Functions ReadFloat and ReadInt64 don't compile in Delphi < 10.3 / 10.4

The functions TNeonDeserializerJSON.ReadFloat and TNeonDeserializerJSON.ReadInt64 in the Source/Neon.Core.Persistence.JSON.pas cause compilation errors in my Delphi version 10.2.

As it turns out System.JSON.TJSONNumber.AsType<T> (line 1523) is only supported from Delphi versions 10.3 up.
See https://docwiki.embarcadero.com/Libraries/Tokyo/en/System.JSON.TJSONNumber_Methods and https://docwiki.embarcadero.com/Libraries/Rio/en/System.JSON.TJSONNumber_Methods

And System.Rtti.TValue.Make<T> (line 1553) is only supported from Delphi 10.4 up.
See https://docwiki.embarcadero.com/Libraries/Rio/en/System.Rtti.TValue.Make and https://docwiki.embarcadero.com/Libraries/Sydney/en/System.Rtti.TValue.Make

Best regards,
Marcus

Cardinal field value which is greater than maxint will lead to convert error.

Unfortunately i don't remember details (fixed that in my project more than 3 months ago) but:

There are problems with deserialization of unsigned types (cardinal/dword/etc).
when you trying to deserialize json to object which has cardinal field and you have numberic value in src json which is greater than maxint - you will receive convert error.

borrowed solution:

unit ReqRes.JSON.Cardinal;

interface
uses
  Neon.Core.Persistence;

procedure RegisterCardinalSerializers(ARegistry: TNeonSerializerRegistry);
implementation
uses
  System.TypInfo,
  System.Json,
  System.Rtti,
  System.SysUtils,
  ReqRes.JSON,
  Neon.Core.Utils,
  Neon.Core.Types,
  Neon.Core.Serializers.RTL;

type
  TCardinalDigestSerializer = 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;

{ TCardinalDigestSerializer }

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

function TCardinalDigestSerializer.Deserialize(AValue: TJSONValue;
  const AData: TValue; ANeonObject: TNeonRttiObject;
  AContext: IDeserializerContext): TValue;
var
  lValue: Cardinal;
begin
  if AValue is TJSONNull then
    Exit(TValue.From<Cardinal>(0));
  lValue := StrToUInt(AValue.Value);
  Result := TValue.From<Cardinal>(lValue);
end;

class function TCardinalDigestSerializer.GetTargetInfo: PTypeInfo;
begin
  Result := System.TypeInfo(Cardinal);
end;

function TCardinalDigestSerializer.Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue;
begin
  Result := TJSONNumber.Create(AValue.AsType<Cardinal>);
end;

procedure RegisterCardinalSerializers(ARegistry: TNeonSerializerRegistry);
begin
  ARegistry.RegisterSerializer(TCardinalDigestSerializer);
end;

end.

we need a TNeon.Print with the param TJSONAncestor.TJSONOutputOptions

e.g.

class function Print(AJSONValue: TJSONValue; OutputOptions: TJSONAncestor.TJSONOutputOptions; APretty: Boolean): string; overload; static;
var
  LWriter: TStringWriter;
begin
  LWriter := TStringWriter.Create;
  try
    TNeon.PrintToWriter(AJSONValue, LWriter, OutputOptions, APretty);
    Result := LWriter.ToString;
  finally
    LWriter.Free;
  end;
end;

class procedure TNeon.PrintToWriter(AJSONValue: TJSONValue; AWriter: TTextWriter;
  OutputOptions: TJSONAncestor.TJSONOutputOptions; APretty: Boolean);
var
  LJSONString: String;
begin
  LJSONString := AJSONValue.ToJSON(OutputOptions);
  PrintToWriter(LJSONString, AWriter, APretty);
end;

Hints and warnings

The library currently compiles with the following hints/warnings:

[dcc32 Warning] Neon.Core.Persistence.JSON.pas(1504): W1002 Symbol 'MinExtended' is specific to a platform
[dcc32 Warning] Neon.Core.Persistence.JSON.pas(1505): W1002 Symbol 'MaxExtended' is specific to a platform
[dcc32 Hint] Neon.Core.Persistence.JSON.pas(1492): H2077 Value assigned to 'LMin' never used
[dcc32 Hint] Neon.Core.Persistence.JSON.pas(1498): H2077 Value assigned to 'LMin' never used
[dcc32 Hint] Neon.Core.Persistence.JSON.pas(1504): H2077 Value assigned to 'LMin' never used
[dcc32 Hint] Neon.Core.Persistence.JSON.pas(1510): H2077 Value assigned to 'LMin' never used
[dcc32 Hint] Neon.Core.Persistence.JSON.pas(1516): H2077 Value assigned to 'LMin' never used
[dcc32 Warning] Neon.Core.Persistence.JSON.pas(1534): W1036 Variable 'LMax' might not have been initialized

Memory leak trying to deserialize a complex object when it comes a null value in a child object

You can test it with the NeonMainDemo project:

  • Go to 'Complex Types' tab.
  • Set {"name": "Paolo", "Note": null} in the Serialize memo.
  • Press Complex Object at Deserialize.
  • Close the application.
  • How can this be avoided?
  • Is there a way to not have to create the properties in the constructor of the parent object?
    TPerson.Create constructor;
    begin
    FNote: = TNote.Create;
    ...

Thanks and great job!

Error Compilig TBase64 class

I had to add 'public' to let 'overload' method accepted. With visibility default 'published' the class did not compile.

TBase64 = class
public
class function Encode(const ASource: TBytes): string; overload;
class function Encode(const ASource: TStream): string; overload;

Readme.MD not updated?

You have an example line in the Readme file that is:
Memo1.Lines.Text := TJSONUtils.PrettyPrint(LJSON);
But it there does not appear to be a PrettyPrint method in the TJSONUtils class. And it is managed by the Serializer Config.

Help to convert json string to object

Please I am trying a single sample using your demo:

  TResponseConsoleMessage = class
  private
    Fname       : String;
    Fvalue      : String;
  public
    property name        : String            read Fname;
    property value       : String            read Fvalue;
end;

Then I use:

procedure TfrmSerializationComplex.Button1Click(Sender: TObject);
var
  LChats: TResponseConsoleMessage;
  LJSON: TJSONValue;
begin
  MemoW.Lines.Text:='{"name":"DomCompleted","value":"teste"}';
  LChats := TResponseConsoleMessage.Create();
  LJSON := TJSONObject.ParseJSONValue(MemoW.Lines.Text);
  try
    TNeon.JSONToObject(LChats, LJSON, frmConfiguration.BuildSerializerConfig);
  finally
    LJSON.Free;
    LChats.Free;
  end;
end;

But the problem is the Object LChats is always empty. The fields contain empty string after to use JsonToObject.

What am I doing wrong?

Multithread - Access violation when a json is deserialized

I'm experiencing an access violation issue when there is a high level of concurrency with multithreading. I have a service that receives JSON data to insert a record into a database table. I'm using Neon to deserialize the JSON and map it to a class.

When I receive a request in my service, I handle it like this:

TTask.Run(
  procedure
  begin
    TPersistencia.Insert(StompFrame.Body, FConf, StompFrame, Self.FStompClient);
  end
);

In my tests, I sent the exact same JSON 1000 times, and occasionally, during the execution of the process, an access violation occurs when TNeonDeserializerJSON attempts to read the JSON to populate the class:

LReader := TNeonDeserializerJSON.Create(LConfig);
LReader.JSONToObject(LClasseTabela, LJSON);  //<< AV occurs here

If I remove TTask, it works fine but is slower, and I need it to be faster. How can I address this problem?

Here is the callstacks of Delphi:
image

There's a way to show null values on DeSerialize ?

Hello Paolo, congratulations on your fantastic library, i believe its the best i've tested so far.
I'm only missing one thing, there's a way to print null values on Deserializing ?

I have a nullable property and i want to show it on json body like "MyProperty": null, its possible ?

i'm trying with the demo, but can't get the null values to show.

paolo

Thank you in advance!

Read static array issue

In Neon.Core.Persistence.JSON.
function : TNeonDeserializerJSON.ReadArray
Lines : 1228

LItemValue := ReadDataMember(LParam, Result, True);
Should be follow:
LItemValue := ReadDataMember(LParam, AData.GetArrayElement(LIndex), True);

Serialization errors on Custom Managed Records with complex types

Add a new field to your TPerson class in Demo.Neon.Entities for this TExampleCMR to replicate "invalid pointer operation" exception on serialization

  TExampleCMR = Record
  Public
    Info:string;
    KeyPairs: TDictionary<string, string>;

    class operator Initialize(out Dest:TExampleCMR);
    class operator Finalize(var Dest:TExampleCMR);
  End;
...

class operator TExampleCMR.Initialize(out Dest:TExampleCMR);
begin
  Dest.KeyPairs := TDictionary<string, string>.Create;
end;
class operator TExampleCMR.Finalize(var Dest:TExampleCMR);
begin
  Dest.KeyPairs.Free;
end;

Failing Tests

When I run the test project I get the following errors:

Failing Tests

  Neon.Tests.Types.Simple.TTestSimpleTypes.TestDateTime.TestDateOnly
  Message: Expected "2019-12-23T00:00:00.000Z" is not equal to actual ""

  Neon.Tests.Types.Simple.TTestSimpleTypes.TestDateTime.TestDateTime
  Message: Expected "2019-12-23T01:01:01.000Z" is not equal to actual ""```

Insufficient RTTI available

Hello,

Please note that this exception is raised when this record needed to be serialized:

TFaceLandmark =
record
  KeyPoints : array[1..68] of TPointFloat;
end;

Project raised exception class EInsufficientRtti with message 'Insufficient RTTI available to support this operation'.

Best regards.

How do I use Arrays and Records ?

Passing a record to TNeon.ObjectToJSON doesn't work, and a class containing an array will not be completely serialised (the array field won't even be in the JSON).

Empty fields

Hello,

Is it possible to include into serialized Json only fields having values different from default (Empty) ones: 0, '', [], ...

Best regards.

Wrong interpretation of [NeonInclude(IncludeIf.NotEmpty)] ?

Hi,
i'm not sure, but from my perpective there is a bug in serialization of properities:

[NeonInclude(IncludeIf.NotEmpty)]
property description: string read Getdescription;

"description" will not be serialized when Getdescription() returns a value ...
I'd expect the serialization of "description" in this case ..
Greetings Andy

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.