Delphi字典保存/加载.TDictionary不可序列化?

Mir*_*láž 2 delphi serialization tdictionary

TDictionary: SaveToFile/LoadFromFile

多么优雅的解决方案!首先,一切都按预期运行.

内容以JSON格式保存到文件中,该格式看起来正确.但重新加载文件后,出现了一个问题:

Type
  TEnumCategTypes = ( e_SQL1, e_VBA, e_Text );
  TCategParams = class
    fontStyles  : TFontStyles;
    rgbColor    : COLORREF;
    end;

  TdictCategory = class ( TDictionary<TEnumCategTypes, TCategParams> )
    public
      public class function LoadFromFile( const AFileName: string ): TdictCategory;
      public class procedure SaveToFile( const AFileName: string; dict: TdictCategory );
    end;

implementation

class procedure TdictCategory.SaveToFile( const AFileName: string; dict: TdictCategory );
var
  stream : TStringStream;
begin
  try
    stream := TStringStream.Create( TJson.ObjectToJsonString( dict ) ) ;
    stream.SaveToFile( AFileName )
  finally
    stream.Free;
  end;
end;
//---
class function TdictCategory.LoadFromFile( const AFileName: string ): TdictCategory;
var
  stream: TStringStream;
begin
  stream   := TStringStream.Create;
  try
    stream.LoadFromFile( AFileName );
    result := TJson.JsonToObject<TdictCategory>( stream.DataString );
  finally
    stream.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

测试如下.所有的荣耀都结束了.这是代码,包括评论:

..
var
  cc: Colorref;
begin
  ..                                                          // fill values 
  cc := DictCategory.Items[ e_SQL1 ].rgbColor;                // Okay, it works
  TdictCategory.SaveToFile( 'category.json', DictCategory );  // Even the contents of the file, looks good 
  DictCategory.Clear;
  DictCategory.Free;
  DictCategory := nil;
  DictCategory := TdictCategory.LoadFromFile( 'category.json' );   // DictCategory is no longer NIL, and it looks optically well..
  cc           := DictCategory.Items[ e2_sql_aggregate ].rgbColor; // C R A S H !!!  with AV
Run Code Online (Sandbox Code Playgroud)

看来Delphi(柏林10.1),无法序列化词典!如果这是真的,那真的会伤害我.我相信还有很多其他人.或者附加代码中是否有任何错误?

Ste*_*nke 6

TJson.JsonToObject最终将使用其默认构造函数实例化对象(请参阅参考资料REST.JsonReflect.TJSONUnMarshal.ObjectInstance).

现在看看System.Generics.Collections,你会发现它TDictionary<TKey,TValue>没有默认构造函数(不,RTTI没有关于参数默认值的信息,因此Capacity: Integer = 0不会考虑构造函数).

这意味着RTTI将进一步查找TObject.Create并在字典类中查找并调用它,这将为您留下一半初始化对象(没有运行您的代码我猜它FComparer没有被分配哪个构造函数TDictionary<TKey,TValue>会完成).

长话短说:为你添加一个无参数构造函数TdictCategory并在inherited Create;那里调用.然后TJSONUnMarshal.ObjectInstance将找到无参数构造函数并调用具有正确初始化实例所需的所有代码.

无论如何,你可能不会对结果感到满意,因为REST.JsonReflect只是序列化实例的所有内部状态(除非通过RTL类中没有完成的属性明确排除),因此也反序列化它们,这意味着这样的JSON只是Delphi- to-Delphi兼容.