Delphi Rest.JSON JsonToObject仅适用于f变量 [英] Delphi Rest.JSON JsonToObject only works with f variables

查看:1037
本文介绍了Delphi Rest.JSON JsonToObject仅适用于f变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Delphi XE8。



我只是看着 REST.Json ObjectToJsonString() JsonToObject()调用。



主要尝试这样做:



如何将对象转换为JSON并使用单行代码返回



我注意到,当他们开始使用 F 字符时,我只能使变量工作。我找不到任何文档。这是预期的行为吗?在开始的时候,我是否应该使用$ code> F 命名所有类的变量?如果是的,有人可以解释为什么吗?



我创建了一个类 TTestJSON 并定义了两个成员变量并将其设置为'WORKS'和'FAILS'。



然后我从对象创建了一个JSON字符串值:

  {
varThatWorksBeacuseItStartsWithF:WORKS,
sVarThatFailsBecauseItStartsWithS:FAILS
}

当从JSON字符串返回到对象时,只有 fVarThatWorksBeacuseItStartsWithF 变量正在正确复位在下面的代码中,使用上述JSON, test:= TJson.JsonToObject< TTestJSON>(JsonStr); 注意到 sVarThatFailsBecauseItStartsWithS 而不是失败

  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;
结束
m1.Lines.Add('** JSONStr Value START **'+#13#10 + JsonStr +'** JSONStr Value END **'+#13#10);

test:= TJson.JsonToObject< TTestJSON>(JsonStr);
try
m1.Lines.Add('** Obj从JSON String开始**'+#13#10 + TJson.ObjectToJsonString(test)+#13#10 +'** Obj加载from JSON String End **');
finally
test.Free;
结束
结束

从结果中,以 f f 从JSON字符串中删除,而以 s 开头的那个仍然存在。我预计第二个结果将如下所示:

  {
varThatWorksBeacuseItStartsWithF:WORKS,
sVarThatFailsBecauseItStartsWithS:FAILS
}

这是完整的代码,只需要一个按钮和vcl表单上的备忘录 - 也使用REST.Json:

  unit Main; 

接口

使用
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;
结束

TForm3 = class(TForm)
btn1:TButton;
m1:TMemo;
procedure btn1Click(Sender:TObject);
private
{私人声明}
public
{公开声明}
end;

var
Form3:TForm3;

执行

{$ R * .dfm}

程序TForm3.btn1Click(发件人: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;
结束
m1.Lines.Add('** JSONStr Value START **'+#13#10 + JsonStr +'** JSONStr Value END **'+#13#10);

test:= TJson.JsonToObject< TTestJSON>(JsonStr);
try
m1.Lines.Add('** Obj从JSON String开始**'+#13#10 + TJson.ObjectToJsonString(test)+#13#10 +'** Obj加载from JSON String End **');
finally
test.Free;
结束
结束

结束。


解决方案

在Delphi中的JSON序列化是基于字段而不是属性。但大多数Delphi类都有友好的属性和F前缀字段。同时似乎Emb试图在生成的JSON中避免使用F前缀的名字。当序列化字段时,它们从名称中切断第一个F,并在从JSON读取时将其添加回(找到正确的字段)。看起来在Delphi中使用JSON序列化的唯一(安全)方法是将所有字段名称保留为前缀F(对于要序列化的字段):

  TTestJSON = class 
protected
FName:String;
public
属性名称:String读取FName写入FName;
结束

更新2:正如David所提到的,我们可以使用属性,然后我们有更好的控制:

 使用
REST.Json.Types,//没有这个单元我们得到警告:W1025不支持的语言功能:'自定义属性'
REST.Json;

type
//所有记录字段都是序列化的,这里没有控制。
TRec = record
RecStr:String;
结束

//默认情况下,类的所有字段都是序列化的,但只有F前缀被正确序列化。
//我们可以使用JSONMarshalled属性来启用/禁用序列化。
//我们可以使用JSonName属性在JSON中将具有特定名称的字段序列化。
TTestJSON = class
[JSONMarshalled(True)] [JSonName('RecField')]
R:TRec;
结束

procedure TForm28.FormCreate(Sender:TObject);
var
测试:TTestJSON;
JsonStr:string;
begin
测试:= TTestJSON.Create;
try
Test.R.RecStr:='Some str';
JsonStr:= TJson.ObjectToJsonString(Test);
finally
FreeAndNil(Test);
结束

// JsonStr:{RecField:[Some str]}

测试:= TJson.JsonToObject< TTestJSON>(JsonStr);
FreeAndNil(Test);
结束


I'm using Delphi XE8.

I was just looking at the REST.Json ObjectToJsonString() and JsonToObject() calls.

Mainly attempting to do something like this:

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

I noticed that I could only get variables to work when they started with an F character. I couldn't find any documentation on this. Is this expected behavior? Should I be naming all variables inside of my classes with an F at the start? If Yes, can someone explain why?

I created a class TTestJSON 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 going back from a JSON string to object, only the fVarThatWorksBeacuseItStartsWithF variable is being reset correctly. In the code below, test := TJson.JsonToObject<TTestJSON>(JsonStr); using the above JSON, notice that the sVarThatFailsBecauseItStartsWithS is "" and 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, the var that starts with f has the f stripped out of the JSON string, and the one that starts with s still has it in there. I would have expected that the second result would have looked like this:

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

Here is the full code to reproduce - just has a button and a memo on a vcl form - also uses REST.Json:

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.

解决方案

JSON serialization in Delphi is based on fields, not properties. But most of Delphi classes have friendly properties and F-prefixed fields. At same time seems Emb is trying to avoid of F-prefixed names in generated JSON. They cut off first "F" from name when serialize fields and add it back (to find correct field) when read from JSON. Seems the only (safe) way to use JSON serialization in Delphi is to keep all field names with prefix "F" (for the fields that 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;

这篇关于Delphi Rest.JSON JsonToObject仅适用于f变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆