Как сохранить объект на диск?
Как хранить объект на диске во всей красе?
Мой объект получен из TObjectList, поэтому он содержит другие объекты.
Какой самый быстрый и простой способ? Какой из них совместим?
Сериализация НЕ является решением, так как я хочу сохранить также непубличные свойства и список объектов, которые он хранит!
На данный момент я пытаюсь сохранить каждый объект независимо как двоичный файл, который затем упаковывается вместе. Это длительный процесс, но позволяет загружать старые версии объекта с более новой версией программы (совместимость с предыдущими сохраненными проектами). В любом случае, сложность начала расти, и она больше не выглядит хорошо.
Ответы
Ответ 1
Я также в основном использую сериализацию вручную для своих собственных структур данных.
- один из основных причин.
Однако в вашем случае это сложно, поскольку не все ваши объекты (tobjectlist) происходят из собственной иерархии, содержащей виртуальные абстрактные методы для загрузки/хранения.
Сериализация D2010 (которую afaik допускает почти все к RTTI) может быть решением, но, вероятно, требует новой версии delphi, и, что еще хуже, она заканчивает ручное управление версиями. (например, копирование значений из старых полей в новые при изменении формата)
Если ручная потоковая передача выходит из-под контроля, другой подход может состоять в том, чтобы иметь абстрактные определения для части данных ваших объектов и генерировать исходный код (объявления полей и потоковый код) из этих абстрактных определений. Преимущество состоит в том, что вы можете пропустить какой-то пользовательский код здесь и там, когда вам нужно, или исправить ваш генератор для проблем с версиями.
Я сделал это один раз для бизнес-объекта для сопоставления SQL с более чем 800 объектами. Поскольку в Delphi было время до дженериков, я создал тип контейнера типов для каждого объекта, а также другие вспомогательные объекты и объекты-подпрограммы.
Очень много работы по настройке, и только стоит того, если у вас есть проект с действительно большим количеством объектов и полей (сотни, если не тысячи), и вы уверены, что вам нужно будет поддерживать его со значительными мутациями в течение довольно долгого времени.
Ответ 2
Если вы используете Delphi 2010, все будет намного проще из-за нового модуля RTTI, Robert Love написал хороший блок для сериализуйте объекты в XML под названием XMLSerial.
Вы можете прочитать об этом в своем блоге: Xml Serialization - Основное использование
Ответ 3
Вы заявляете, что сериализация не является решением, но я спрашиваю, почему нет? Я сделал что-то подобное в прошлом, но вот что я сделал.
Я создал класс компонентов, который ничего не делал, но сериализовал объект, не относящийся к TPersistant, чтобы я мог его транслировать и использовать с помощью возможностей потоковой передачи VCL.
Например:
//Пожалуйста, простите меня за любые ошибки, которые существуют, поскольку я пытаюсь набрать это из верхней части головы. Кроме того, это не будет функционально полным.
unit streamlist1;
interface
uses MyListObjectUnit;
procedure SaveList(fielname:string; data:TMyListObject);
procedure LoadList(filename:string; var data:TMyListObject);
implementation
type
TMyListStreamer = class(TComponent)
private
fMyList : TMyListObject;
procedure ReadList(Reader:TReader); //This is where the magic happens
procedure WriteList(Writer: TWriter); //This is where the magic happens (x2)
public
procedure DefineProperties(Filer: TFiler); override; //defined in TPersistent
procedure AssignMyList(data:TMyListObject);
procedure PopulateData(var data:TMyListObject);
end;
TMyListStreamer.procedure DefineProperties(Filer: TFiler); override; //defined in TPersistent
begin
Filer.DefineProperty('MyObjList', ReadList, WriteList, true);
//Filer.DefineBinaryProperty('MyObjList', ReadList, WriteList, true); //your choice
end;
procedure TMyListStreamer.ReadList(Reader:TReader); //This is where the magic happens
begin
//Use the reader class to read in anything you want...
end;
procedure TMyListStreamer.WriteList(Writer: TWriter); //This is where the magic happens (x2)
begin
//Use the writer class to write out anything you want...
end;
procedure SaveList(fielname:string; data:TMyListObject);
var
wFile : TFileStream;
wList : TMyListStreamer;
begin
RegisterClass(TMyListStreamer);
Try
wFile := TFileStream.Create(filename, fmcreate);
wList := TMyListStreamer.create(nil);
try
wList.AssignMyList(Data);
wFile.WriteComponent(wList);
finally
wFile.Free;
wList.free;
end;
finally
Unregisterclass(TMyListStreamer);
end;
end;
procedure LoadList(filename:string; var data:TMyListObject);
var
wFile : TFileStream;
wList : TMyListStreamer;
begin
RegisterClass(TMyListStreamer);
Try
wFile := TFileStream.Create(filename, fmOpenRead);
try
wList := TMyListStreamer(wFile.ReadComponent(Nil));
if assigned(data) and assigned(wList) then
wList.PopulateData(data);
if assigned(wList) then
wList.free;
finally
wFile.Free;
end;
finally
Unregisterclass(TMyListStreamer);
end;
end;
Используя этот метод, вы можете передавать (сериализовать) что-либо из VCL или пользовательских данных.
Требуется немного, чтобы настроить, но сила в том, что вы можете контролировать все, что происходит и выходит из файла данных. Вы даже можете подумать, создать флаг версии и обработать разные данные, игнорируя или массируя определенные данные в новых версиях программы/компонента.
Вы даже можете передавать другие объекты VCL из своего потокового компонента, если вы уже знаете тип объекта (т.е. объекты на основе TComponent/TPersistant), используя существующие методы TReader/TWriter.
Не полное решение, но оно должно помочь вам, если вы захотите пойти с еще большей работой.
Ответ 4
Ваше текущее решение, вероятно, лучше всего, если вы собираетесь иметь отдельные версии объекта на протяжении многих лет.
Я создаю методы SaveToStream() и LoadFromStream() и вручную записываю свойства объекта в поток в фиксированном порядке, префикс его с номером версии структуры. Преимущество этого, как вы говорите, в том, что вы можете лучше адаптироваться к более старым версиям потока. Например, если у вас есть 5 версий, но вам необходимо что-то инициализировать для файлов версии 3, это легко сделать. Затем вы обертываете вокруг него функцию SaveToFile(), которая создает TFileStream и вызывает SaveToStream().
Я считаю, что есть класс TWriter, который позволяет вам писать различные типы данных в поток более легко... или вы можете создать свой собственный достаточно просто. (Я сделал свой собственный потомок потомков, чтобы справиться с этим)
Если вы сохраняете несколько объектов в одном потоке, вы можете захотеть отметить позицию до того, как каждый объект будет написан, а затем вернитесь и отметьте длину, чтобы вы (или кто-то, обращающийся к файлам) могли пропустить вперед файл не читая его.
Кроме того, если у вас есть иерархия классов, которые вы хотите сохранить, "нижняя загрузка" класса ancester со всеми свойствами, которые вы хотите сохранить в файле. Таким образом, вам нужна только одна реализация процедуры сохранения. Это немного менее эффективно, так как вы несете переменные, которые вам не обязательно нужны во всех объектах, но их гораздо проще управлять.
Ответ 5
На данный момент я пытаюсь сохранить каждый объект независимо как двоичный файл которые затем упаковываются вместе.
Я думаю, что это хороший подход. Это может потребовать немного кодирования, но это быстро! Кроме того, вы можете легко легко обновить его, если вам нужно внести изменения в свой формат файла. Для упрощенного обновления формата не забудьте оставить в вашем файле некоторые пробельные байты. Затем вы можете добавить дополнительную информацию без фактического изменения формата файла.
Ответ 6
Вам нужно придумать какой-то способ кодирования этого объекта в виде строки, чтобы при создании нового объекта с этой строкой вы получали один и тот же объект. Это называется сериализацией, и некоторые языки предоставляют вам эту функциональность.
Например, если у вас есть этот объект:
class serialize_me {
private:
int a;
float b;
public:
double c;
}
и a = 5, b = 3,2 и c = 67,5
ваша строка может выглядеть так:
a5b3.2c67.5
Затем вы можете разобрать эту строку и назначить соответствующие значения всем членам.
Я думаю, само собой разумеется, что строки легко хранить на диске.
EDIT:
Это очень простой пример, но я думаю, что вы легко поняли концепцию.
EDIT:
Серийная настройка Delphi. В нижней части страницы есть ссылка на полноценный класс XML-сериализатора.
Ответ 7
Множество способов сделать это, я думаю, в прошлом я использовал inifiles для этой цели, полезный, поскольку TMemInifile - это для вас, чтобы вы много делали работу в памяти, прежде чем сохранять на диск. Поскольку у вас есть иерархия для сохранения, вы можете захотеть использовать что-то вроде XML, но я этого не сделал лично, поэтому не могу посоветовать это.
Ответ 8
Если объект получен из протокола TPersistent, XML или JSON, легко сделать с библиотеками с открытым исходным кодом:
Одним из способов упрощения управления версиями является уровень защиты от коррупции между моделью домена и уровнем сохранения. (возможно, используя объекты передачи данных, которые не будут меняться при каждом изменении модели домена).
Для автоматического контроля версий см. эту статью: Перенос сериализованных объектов Java с помощью XStream и XMT
XMT вводит класс VersionedDocument в сериализованные XML-версии и обрабатывает миграцию. Та же конструкция может быть легко реализована с помощью Delphi.