当前位置: 首页 > 工具软件 > SuperObject > 使用案例 >

使用superobject中的TSuperObjectHelper序列化和反序列化对象

须景胜
2023-12-01

最近在研究把对象进行序列化和反序列化,看过网上很多办法,

1.TStream.ReadComponent和TStream.WriteComponent的方法;

2.XML和对象的转换。

3.Json和对象的转换。

TStream的方法太过简单,经使用只能对Component的属性或事件进行记录,如果对象中存在私有变量的非属性字段,那就没办法序列化,不能达到效果。也可能因我没有深究,没有找到实现办法。
XML和对象进行转换的方法我没有去研究,这里就不做解释。
superobject应该说是很牛逼的json对象了,大家应该比较熟悉,使用网上资料也非常多,superobject单元中有一个TSuperObjectHelper,估计大家都没使用过,在网上的资料也甚少,在万一博客中也没见到提及,好奇的研究了一下这个。

“` ruby
Uses superobject;

TTest = class(TObject)
test : TObject;
a,b,c,d: string;
public
constructor Create;overload;
destructor Destroy; override;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
test : TTest;
begin
test := TTest.Create;
test.a := ‘a’;
test.b := ‘b’;
test.c := ‘c’;
test.d := ‘d’;
Memo1.Text := test.ToJson().AsString;
test.Destroy;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
test : TTest;
begin
test := TTest.FromJson(Memo1.Text);
showmessage(test.a + #13#10 + test.b + #13#10 + test.c + #13#10 + test.d);
test.Destroy;
end;

“`

上面是简单的代码可以直接使用,可以尝试各种对象都能正常序列化和反序列化。
因为项目需要,在项目中涉及到泛型数据,这样,按照上面的代码转换,结果就懵逼了,各种报错。

TTestMemberObject = class(TObject)
a : string;
public
constructor Create;overload;
end;

TTestClass = class(TObject)
a,b : string;
c : TMsObjectList; //这里是我自己写的一个继承THashStringList的对象,并引入泛型思路,可以保存任何对象
public
constructor Create;overload;
destructor Destroy; override;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
test : TTestClass;
t : TTestMemberObject;
begin
test := TTestClass.Create;
test.a := ‘TEST_A’;
test.b := ‘TEST_B’;
t := TTestMemberObject.Create;
t.a := t.ClassName + ‘_A’;
test.c.AddObject(t.a,t);
Memo1.Text := test.ToJson().AsString;
test.Destroy;

end;

procedure TForm1.Button2Click(Sender: TObject);
var
test : TTestClass;
begin
test := TTestClass.FromJson(Memo1.Text);
ShowMessage(test.c.Objects[0].a);
test.Destroy;
end;

以上的代码各种报错,报错是因为我的THashStringList中因考虑到多线程用到了临界区变量,里面在序列化的时候涉及到指针类型,而superjson中的FromJson中看到源码tkPointer没有操作返回False报错。经过查看源码,看到ToJson和FromJSon中有传一个参数TSuperRttiContext,superjson作者也已经考虑因项目各异,需要自己来重写转换代码了。

代码如下:

TMsSuperRttiContext = class(TSuperRttiContext)
protected
function FromJson(TypeInfo: PTypeInfo; const obj: ISuperObject; var Value: TValue): Boolean; override;
function ToJson(var value: TValue; const index: ISuperObject): ISuperObject; override;
end;

覆盖FromJson和ToJson方法。

在FromJson中写
function TMsSuperRttiContext.FromJson(TypeInfo: PTypeInfo;
const obj: ISuperObject; var Value: TValue): Boolean;
begin
Result := inherited;

if Result = False then

if TypeInfo <> nil then
begin
  if not SerialFromJson.TryGetValue(TypeInfo, Serial) then
    case TypeInfo.Kind of

      tkMethod,tkPointer,tkInterface: Result := True;

    end;
end else
  Result := False;

end;

加判断如果TryGetValue为tkMethod,tkPointer,tkInterface,强制返回True.

修改代码
procedure TForm1.Button2Click(Sender: TObject);
var
test : TTestClass;
context: TMsSuperRttiContext;
begin
context := TMsSuperRttiContext.Create;
test := TTestClass.FromJson(Memo1.Text,context);
ShowMessage(test.c.Objects[0].a);
ShowMessage(test.c.Count.ToString);
test.Destroy;
context.Destroy;
end;

现在不报错了,能正常运行,但是可惜显示的test.c.Objects[0].a为空,这不是我想要的结果,这就是因为我使用的泛型,而THashStringList的Objects的List对象是TObject的,反序列化时Rtti解析成TObject对象,不会解析成我需要的TTestMemberObject对象,现在只能在反序列化时的子对象中强转成TTestMemberObject对象即可,修改代码如下:
function TMsSuperRttiContext.ToJson(var value: TValue; const index: ISuperObject): ISuperObject;
var
s : string;
begin
Result := inherited;
try
s := value.TypeInfo.Name;
if s = ‘TTestMemberObject’ then
Result.S[‘class_type’] := ‘TTestMemberObject’;
except
on e: Exception do
ShowMessage(e.Message);
end;
end;
这里是在序列化的时候,让json对象记录一下这个对象的类型名称,然后在反序列化时判断这个对象名称时再转换回来。
function TMsSuperRttiContext.FromJson(TypeInfo: PTypeInfo; const obj: ISuperObject; var Value: TValue): Boolean;
var
s : string;
Serial : TSerialFromJson;
o : TTestMemberObject;
begin
if obj.S[‘class_type’] = ‘TTestMemberObject’ then begin

o := TTestMemberObject.Create;
Result := inherited FromJson(TValue(o).TypeInfo,obj,Value);//这里必须需要O对象,不能通过类名直接转,暂时还没找到其他办法,只能先这么用着了。
o.Destroy;

end else
Result := inherited;

if Result = False then

if TypeInfo <> nil then
begin
  if not SerialFromJson.TryGetValue(TypeInfo, Serial) then
    case TypeInfo.Kind of

      tkMethod,tkPointer,tkInterface: Result := True;

    end;
end else
  Result := False;

end;
记住在序列化的时候要传入对象:
procedure TForm1.Button1Click(Sender: TObject);
var
test : TTestClass;
t : TTestMemberObject;
context : TMsSuperRttiContext;
begin
context := TMsSuperRttiContext.Create;
test := TTestClass.Create;
test.a := ‘TEST_A’;
test.b := ‘TEST_B’;
t := TTestMemberObject.Create;
t.a := t.ClassName + ‘_A’;
test.c.AddObject(t.a,t);
Memo1.Text := test.ToJson(context).AsString;
test.Destroy;
context.Destroy;

end;

经过继承对象,覆盖两个方法,解决使用泛型的问题,可以自由让对象转换成Json字符串,并可以完全把Json反转换成对象了。

 类似资料: