以下代码来自Marco Cantu的Delphi 2010手册第7章中的JSonMarshall项目。源代码可从此处http://cc.embarcadero.com/item/27600获得。我对它做了两处修改:
将JSon添加到实现Uses子句中以使其编译。
添加了一行
theName:='XXX'; //由我添加
到TDataWithList.Create
构造函数以协助调试
我在Delphi Seattle中运行代码(没有更新1)
该项目的目的是演示TDataWithList声明类型的自定义转换器和恢复器。自定义转换器似乎工作正常,从结果输出到Memo1
判断。
然而,尝试运行回复器导致线路上的“读取地址00000000”AV
sList.Add (Args[I]);
btnUnmarshalReverterClick
中的。造成这种情况的直接原因是与此相反 作者明显打算,当上面的行执行时,sList是Nil。
我的问题是为什么sList Nil以及如何解决这个问题?
我已经尝试过,但并非完全成功,可以追踪DBXJSONReflect源代码 找出原因。
在
Obj := ObjectInstance(FRTTICtx, objType);
函数TJSONUnMarshal.CreateObject
中的,TDataWithList(obj).theName是'XXX' 正如我所料和TDataWithList(obj).theLList是一个初始化但空的, TStringList中。
但是,当调用btnUnmarshalReverterClick
中的匿名方法时,TDataWithList(Data).TheList为Nil
。
更新: TDataWithList(Data).theList(错误地,imo)变为Nil的原因是它通过调用PrePopulateObjField在TJSONPopulationCustomizer.PrePopulate中设置为Nil。所以我想问题是,为什么PrePopulate允许在其构造函数中初始化的对象字段被覆盖,好像它更好地知道对象的构造函数。
更新2:
据我所知,可能存在另外一个问题
TInternalJSONPopulationCustomizer.PrePopulateObjField
,用Nil覆盖TListWithData.theList的赋值,即
rttiField.SetValue(Data, TValue.Empty);
似乎没有导致调用TStringlist析构函数。
顺便说一下,我在XE4中运行项目得到了同样的错误,这是我最早的版本,包括JSonUnMarshal。
代码:
type
[...]
TDataWithList = class
private
theName: String;
theList: TStringList;
public
constructor Create (const aName: string); overload;
constructor Create; overload;
function ToString: string; override;
destructor Destroy; override;
end;
[...]
procedure TFormJson.btnMarshalConverterClick(Sender: TObject);
var
theData: TDataWithList;
jMarshal: TJSONMarshal;
jValue: TJSONValue;
begin
theData := TDataWithList.Create('john');
try
jMarshal := TJSONMarshal.Create(
TJSONConverter.Create); // converter is owned
try
jMarshal.RegisterConverter(TDataWithList, 'theList',
function (Data: TObject; Field: string): TListOfStrings
var
I: Integer;
sList: TStringList;
begin
sList := TDataWithList(Data).theList;
SetLength(Result, sList.Count);
for I := 0 to sList.Count - 1 do
Result[I] := sList[I];
end);
jValue := jMarshal.Marshal(theData);
try
Memo1.Lines.Text := jValue.ToString;
finally
jValue.Free;
end;
finally
jMarshal.Free;
end;
finally
theData.Free;
end;
end;
procedure TFormJson.btnUnmarshalReverterClick(Sender: TObject);
var
jUnmarshal: TJSONUnMarshal;
jValue: TJSONValue;
anObject: TObject;
begin
jValue := TJSONObject.ParseJSONValue(
TEncoding.ASCII.GetBytes (Memo1.Lines.Text), 0);
try
jUnmarshal := TJSONUnMarshal.Create;
try
jUnmarshal.RegisterReverter(TDataWithList, 'theList',
procedure (Data: TObject; Field: string; Args: TListOfStrings)
var
I: Integer;
sList: TStringList;
begin
sList := TDataWithList(Data).theList;
for I := 0 to Length(Args) - 1 do
sList.Add (Args[I]);
end);
anObject := jUnmarshal.Unmarshal(jValue);
try
ShowMessage ('Class: ' + anObject.ClassName +
sLineBreak + anObject.ToString);
finally
anObject.Free;
end;
finally
jUnmarshal.Free;
end;
finally
jValue.Free;
end;
end;
function TMyData.ToString: string;
begin
Result := theName + ':' + IntToStr (theValue);
end;
{ TDataWithList }
constructor TDataWithList.Create(const aName: string);
var
I: Integer;
begin
theName := aName;
theList := TStringList.Create;
for I := 0 to 9 do
theList.Add(IntToStr (Random (1000)));
end;
constructor TDataWithList.Create;
begin
// core initialization, used for default construction
theName := 'XXX'; // added by me
theList := TStringList.Create;
end;
destructor TDataWithList.Destroy;
begin
theList.Free;
inherited;
end;
function TDataWithList.ToString: string;
begin
Result := theName + sLineBreak + theList.Text;
end;
答案 0 :(得分:3)
rttiField.SetValue(Data, TValue.Empty);
只是覆盖字段值,因为顾名思义它是一个字段,而不是具有get / set方法的属性。由于简单的指针赋值,不会调用TStringList的析构函数。
这里的解决方案是声明一个属性:
TDataWithList = class
...
strict private
theList: TStringList;
...
public
property Data: TStringList read ... write SetData
...
end;
TDataWithList.SetData(TStringList aValue);
begin
theList.Assign(aValue);
end;