如何在 Delphi 10.1 berlin 中将嵌套对象转换为 Json

How to convert nested object into Json in Delphi 10.1 berlin

我是 delphi 的新手。目前我面临一个问题。我想使用 TJson 将嵌套对象转换为 Json,但存在与内存相关的问题。

这是我的代码。

它只是一个简单的单元文件,包含人员和地址 class。人 class 取决于地址 class。

unit uPerson;

interface

uses
  REST.Json;

type
  TAddress = class
  private
    FStreetNAme: string;
    FState: string;
    FPinCode: string;
  published
    property StreetNAme: string read FStreetNAme write FStreetNAme;
    property State: string read FState write FState;
    property PinCode: string read FPinCode write FPinCode;
  end;

  TPerson = class
  private
    FName: string;
    FAge: Integer;
    FSalary: Double;
    [JSONMarshalled(True)]
    FAddress: TAddress;
  published
    property Name: string read FName write FName;
    property Age: Integer read FAge write FAge;
    property Salary: Double read FSalary write FSalary;
    property Address: TAddress read FAddress write FAddress;
  end;

implementation

end.

下面是主窗体代码

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, uPerson,
  REST.JSON;

type
  TForm1 = class(TForm)
    edtName: TLabeledEdit;
    edtAge: TLabeledEdit;
    edtSalary: TLabeledEdit;
    edtStreet: TLabeledEdit;
    edtState: TLabeledEdit;
    edtPin: TLabeledEdit;
    btnSave: TButton;
    Memo1: TMemo;
    procedure btnSaveClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


var
  Form1: TForm1;
  Person: TPerson;
  Add: TAddress;

implementation

{$R *.dfm}

procedure TForm1.btnSaveClick(Sender: TObject);
var
  jsonString: string;
begin
  Person := TPerson.Create;
  try
    Person.Name := edtName.Text;
    Person.Age := Integer.Parse(edtAge.Text);
    Person.Salary := double.Parse(edtSalary.Text);

    Add.StreetNAme := edtStreet.Text;
    Add.State := edtState.Text;
    Add.PinCode := edtPin.Text;

    Person.Address := Add;

    jsonString := TJson.ObjectToJsonString(Person);
    Memo1.Text := jsonString;

  finally
    Add.Free;
    Person.Free;
  end;
  //
end;

end.

代码正在正确编译。但是当尝试生成 json 时,它给出了访问冲突错误。这是图像 - access violation error image

[更新] 基本上我得到“0x00409fca 的访问冲突:地址 0x00000004 的写入”。

提前致谢。

我对 JSON 设施一无所知,但我知道 Delphi 内存管理。

这个错误是预料之中的,因为你忘记创建 TAddress 对象。

第一个问题是这一行:

Add.StreetNAme := edtStreet.Text;

Add 是一个全局变量,所以它最初被设置为 nil (因为它是一个对象指针)。因此,您在这里尝试写入非常接近 0 的内存地址,这正是您在异常消息中看到的内容。

你需要在堆上创建一个TAddress对象,并将这个对象的地址赋给Add变量。

就像您对 TPerson 对象所做的那样。

procedure TForm1.btnSaveClick(Sender: TObject);
var
  jsonString: string;
begin
  Person := TPerson.Create;
  try
    Add := TAddress.Create;
    try
      Person.Name := edtName.Text;
      Person.Age := Integer.Parse(edtAge.Text);
      Person.Salary := double.Parse(edtSalary.Text);
  
      Add.StreetName := edtStreet.Text;
      Add.State := edtState.Text;
      Add.PinCode := edtPin.Text;
  
      Person.Address := Add;
  
      jsonString := TJson.ObjectToJsonString(Person);
      Memo1.Text := jsonString;
    finally
      Add.Free;
    end;
  finally
    Person.Free;
  end;
end;

此外,在这里使用全局变量也不是一个好主意。相反,使用局部变量。而且根本不需要单独的 TAddress 变量:

var
  Person: TPerson;
  jsonString: string;
begin
  Person := TPerson.Create;
  try
    Person.Address := TAddress.Create;
    try
      Person.Name := edtName.Text;
      Person.Age := Integer.Parse(edtAge.Text);
      Person.Salary := double.Parse(edtSalary.Text);

      Person.Address.StreetName := edtStreet.Text;
      Person.Address.State := edtState.Text;
      Person.Address.PinCode := edtPin.Text;

      jsonString := TJson.ObjectToJsonString(Person);
      Memo1.Text := jsonString;
    finally
      Person.Address.Free;
    end;
  finally
    Person.Free;
  end;
end;

此外,您可能会争辩说,如果 TPerson 构造函数创建一个 TAddress 对象并将指向它的指针放在其 Address 字段中会更好。那么 TPerson 析构函数也将负责释放这个对象:

unit uPerson;

interface

uses
  REST.Json;

type
  TAddress = class
  private
    FStreetNAme: string;
    FState: string;
    FPinCode: string;
  published
    property StreetNAme: string read FStreetNAme write FStreetNAme;
    property State: string read FState write FState;
    property PinCode: string read FPinCode write FPinCode;
  end;

  TPerson = class
  private
    FName: string;
    FAge: Integer;
    FSalary: Double;
    [JSONMarshalled(True)]
    FAddress: TAddress;
  public
    constructor Create;
    destructor Destroy; override;
  published
    property Name: string read FName write FName;
    property Age: Integer read FAge write FAge;
    property Salary: Double read FSalary write FSalary;
    property Address: TAddress read FAddress write FAddress;
  end;

implementation

{ TPerson }

constructor TPerson.Create;
begin
  FAddress := TAddress.Create;
end;

destructor TPerson.Destroy;
begin
  FAddress.Free;
  inherited;
end;

end.

var
  Person: TPerson;
  jsonString: string;
begin
  Person := TPerson.Create;
  try
    Person.Name := 'Andreas';
    Person.Age := 32;
    Person.Salary := 12345;

    Person.Address.StreetName := 'Street';
    Person.Address.State := 'State';
    Person.Address.PinCode := 'pin';

    jsonString := TJson.ObjectToJsonString(Person);
    Memo1.Text := jsonString;
  finally
    Person.Free;
  end;
end;