Delphi Dictionary Сохранить / Загрузить. 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;

Тест следует. И вся слава заканчивается. Вот код, включая комментарий:

..
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

Похоже, что Delphi (Berlin 10.1) не может сериализовать словарь! Если это правда, это действительно причиняет мне боль. Я верю, что есть много других. Или есть ошибка в прикрепленном коде?

1 ответ

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.

Другие вопросы по тегам