问题是这样的。
在我的程序中,我用json存放用户的设置,保存到文件中。
当我的程序版本升级时,我往用户的设置中增加了新的Fields。当我从用户的机器中读取老版本的设置时,会出现错误。我的读取与写入代码如下:
procedure ReadFromStream<T>(Stream: TStream; var O: T);
var
CTX:TSuperRttiContext;
Size:Cardinal;
StrStream:TStringStream;
begin
CTX:=TSuperRttiContext.Create;
StrStream:=TStringStream.Create;
try
Stream.Read(Size,SizeOf(Size));
StrStream.CopyFrom(Stream,Size);
O:=CTX.AsType<T>(SO(StrStream.DataString));
finally
CTX.Free;
StrStream.Free;
end;
end;
procedure WriteToStream<T>(Stream: TStream; O: T);
var
CTX:TSuperRttiContext;
Size:Cardinal;
StrStream:TStringStream;
begin
CTX:=TSuperRttiContext.Create;
StrStream:=TStringStream.Create;
try
StrStream.WriteString(CTX.AsJson<T>(O).AsJSon);
Size:=StrStream.Size;
StrStream.Position:=0;
Stream.Write(Size,SizeOf(Size));
Stream.CopyFrom(StrStream,Size);
finally
CTX.Free;
StrStream.Free;
end;
end;
当我调用 ReadFromStream 去读取用户设置时, 在这行将发生错误Marshalling error :O:=CTX.AsType<T>(SO(StrStream.DataString)) .
为了解决这个问题,我对TSuperRttiContext.FromJson方法进行了修改:
procedure FromRecord;
var
f: TRttiField;
p: Pointer;
v: TValue;
begin
Result := True;
TValue.Make(nil, TypeInfo, Value);
for f in Context.GetType(TypeInfo).GetFields do
begin
if ObjectIsType(obj, stObject) and (f.FieldType <> nil) then
begin
p := IValueData(TValueData(Value).FHeapData).GetReferenceToRawData;
Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v);
//modified start
if not Result then
TValue.Make(nil, f.FieldType.Handle, v);
f.SetValue(p, v);
Exit;
// modified end
end else
begin
Result := False;
Exit;
end;
end;
end;
procedure FromClass;
var
f: TRttiField;
v: TValue;
begin
case ObjectGetType(obj) of
stObject:
begin
Result := True;
if Value.Kind <> tkClass then
Value := GetTypeData(TypeInfo).ClassType.Create;
for f in Context.GetType(Value.AsObject.ClassType).GetFields do
if f.FieldType <> nil then
begin
Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v);
{Modified by neugls 2011/4/5}
//modified start
if not Result then
TValue.Make(nil, f.FieldType.Handle, v);
f.SetValue(Value.AsObject, v);
Exit;
//modified end
end;
end;
stNull:
begin
Value := nil;
Result := True;
end
else
// error
Value := nil;
Result := False;
end;
end;
总结:问题的根源在于,如果ISuperObject 实例中没有与TypeInfo对应的field话,就会失败,而我的修改就是绕过了这个!