Delphi Rest.JSON JsonToObject only works with f variables

I am using Delphi XE8.

I just looked at the calls to REST.Json ObjectToJsonString() and JsonToObject() .

Basically an attempt to do something like this:

How to convert an object to JSON and back with a single line of code

I noticed that I can make variables work when they start with the F character. I could not find documentation about this. Is this expected behavior? Should I specify all the variables inside my classes with F at the beginning? If so, can someone explain why?

I created the TTestJSON class and defined two member variables and set them to "WORKS" and "FAILS".

Then I created a JSON string value from the object:

 { "varThatWorksBeacuseItStartsWithF":"WORKS", "sVarThatFailsBecauseItStartsWithS":"FAILS" } 

When only fVarThatWorksBeacuseItStartsWithF returned from the JSON string to the object, the reset variable is correct. In the code below test := TJson.JsonToObject<TTestJSON>(JsonStr); using the above JSON, note that sVarThatFailsBecauseItStartsWithS is "" , not "FAILS" .

 procedure TForm3.btn1Click(Sender: TObject); var test : TTestJSON; JsonStr : String; begin m1.Clear; test := TTestJSON.Create; try test.fVarThatWorksBeacuseItStartsWithF := 'WORKS'; test.sVarThatFailsBecauseItStartsWithS := 'FAILS'; JsonStr := TJson.ObjectToJsonString( test ); finally test.Free; end; m1.Lines.Add( '** JSONStr Value START **' + #13#10 + JsonStr + '** JSONStr Value END **' + #13#10 ); test := TJson.JsonToObject<TTestJSON>(JsonStr); try m1.Lines.Add('** Obj loaded from JSON String Start **' + #13#10 + TJson.ObjectToJsonString( test ) + #13#10 + '** Obj loaded from JSON String End **'); finally test.Free; end; end; 

From the results, var starting with F has F removed from the JSON string, and one that starts with s still has it there. I would expect the second result to look like this:

 { "varThatWorksBeacuseItStartsWithF":"WORKS", "sVarThatFailsBecauseItStartsWithS":"FAILS" } 

Here is the complete code to play - just have a button and note in vcl form - REST.Json is also used:

 unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, rest.Json; type TTestJSON = class fVarThatWorksBeacuseItStartsWithF : String; sVarThatFailsBecauseItStartsWithS : String; end; TForm3 = class(TForm) btn1: TButton; m1: TMemo; procedure btn1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form3: TForm3; implementation {$R *.dfm} procedure TForm3.btn1Click(Sender: TObject); var test : TTestJSON; JsonStr : String; begin m1.Clear; test := TTestJSON.Create; try test.fVarThatWorksBeacuseItStartsWithF := 'WORKS'; test.sVarThatFailsBecauseItStartsWithS := 'FAILS'; JsonStr := TJson.ObjectToJsonString( test ); finally test.Free; end; m1.Lines.Add( '** JSONStr Value START **' + #13#10 + JsonStr + '** JSONStr Value END **' + #13#10 ); test := TJson.JsonToObject<TTestJSON>(JsonStr); try m1.Lines.Add('** Obj loaded from JSON String Start **' + #13#10 + TJson.ObjectToJsonString( test ) + #13#10 + '** Obj loaded from JSON String End **'); finally test.Free; end; end; end. 
+6
source share
2 answers

Delphi's JSON serialization is based on fields, not properties. But most Delphi classes have friendly properties and F-prefixed fields. At the same time, it seems that Emb is trying to avoid the F-prefixed names in the generated JSON. They first cut off the “F” from the name when serializing the fields and add it back (to find the correct field) when reading from JSON. It seems the only (safe) way to use JSON serialization in Delphi is to keep all field names prefixed with "F" (for the fields you want to serialize):

 TTestJSON = class protected FName: String; public property Name: String read FName write FName; end; 

UPDATE2: As David mentions, we can use attributes, and then we have much better control:

 uses REST.Json.Types, // without this unit we get warning: W1025 Unsupported language feature: 'custom attribute' REST.Json; type // All fields of records are serialized, no control here. TRec = record RecStr: String; end; // By default all fields of class are serialized, but only F-prefixed serialized correctly. // We can use JSONMarshalled attribute to enable/disable serialization. // We can use JSonName attribute to serialize field with specific name in JSON. TTestJSON = class [JSONMarshalled(True)] [JSonName('RecField')] R: TRec; end; procedure TForm28.FormCreate(Sender: TObject); var Test: TTestJSON; JsonStr: string; begin Test := TTestJSON.Create; try Test.R.RecStr := 'Some str'; JsonStr := TJson.ObjectToJsonString( Test ); finally FreeAndNil(Test); end; // JsonStr: {"RecField":["Some str"]} Test := TJson.JsonToObject<TTestJSON>(JsonStr); FreeAndNil(Test); end; 
+3
source

This library serializes fields. Since it is common practice to prefix fields with the letter F, developers want to remove this letter from the names used in JSON. So they decided to make the default behavior consist of deleting the first letter in the fields whose name begins with F. Frankly, this seems rather strange to me.

This behavior can be configured using attributes. For instance:

 [JSONMarshalled(False)] FFoo: Integer; [JSONMarshalled(True)] [JSONName('Blah')] Bar: Integer; 

As far as I can see, none of this is documented.

+3
source

All Articles