2010-02-28 16 views
14

Wie wird ein Objekt in seiner ganzen Pracht auf Festplatte gespeichert? Mein Objekt wird von TObjectList abgeleitet, sodass es andere Objekte enthält.Wie wird ein Objekt auf Festplatte gespeichert?

Welcher ist der schnellste und einfachste Weg? Welches ist der kompatible Weg?

Serialisierung ist keine Lösung, da ich auch nicht öffentliche Eigenschaften und die Liste der Objekte, die es enthält, speichern möchte!

Für den Moment versuche ich jedes Objekt unabhängig als Binärdatei zu speichern, die dann zusammen gepackt werden. Dies ist ein langwieriger Prozess, aber ich kann alte Versionen des Objekts mit einer neueren Version des Programms laden (Kompatibilität mit zuvor gespeicherten Projekten). Wie auch immer, die Komplexität begann zu wachsen und es sieht nicht mehr gut aus.

+1

+1 das ist eine sehr gute Frage, in RTTI, Meta-Daten nur für veröffentlichte Abschnitt Felder verfügbar, aber von anderen Seite zeigt Debug-Inspektor alle Klassenfelder, so muss es eine Möglichkeit sein, dies zu tun. –

+0

Warum sollten Sie eine Datei pro Objekt verwenden? Das wird natürlich langsamer und fehleranfälliger. – mghie

+0

"Warum sollten Sie eine Datei pro Objekt verwenden?" ---- Du hast recht, aber ich bin erst am Anfang davon. Es wird sich auf die eine oder andere Weise zu einer einzigen Datei entwickeln. – Ampere

Antwort

2

ich meist auch handwerklich Serialisierung für meine eigenen Datenstrukturen verwenden. Der Mehrfachversionswinkel ist einer der Hauptgründe.

In Ihrem Fall ist das jedoch schwierig, da nicht alle Ihre Objekte (tobjectlist) von einer eigenen Hierarchie abgeleitet sind, die virtuelle abstrakte Methoden zum Laden/Speichern enthalten.

D2010 Serialisierung (die afaik erlaubt fast alles zu RTTI) könnte eine Lösung sein, aber wahrscheinlich erfordert eine neue Delphi-Version, und noch schlimmer, es buchstabiert das Ende der manuellen Umgang mit Versionierung. (zB Kopieren von Werten aus alten Feldern in neue, wenn sich das Format ändert)

Wenn das manuelle Streaming aus dem Ruder läuft, könnte ein anderer Ansatz darin bestehen, abstrakte Definitionen für den Datenteil Ihrer Objekte zu haben und Quellcode zu generieren (die Felddeklarationen und der Streaming-Code) aus diesen abstrakten Definitionen. Der Vorteil ist, dass Sie möglicherweise hier und da einen benutzerdefinierten Code einfügen können, wenn Sie dies benötigen, oder Ihren Generator für Probleme mit der Versionierung patchen.

Ich habe dies einmal für ein Business-Objekt zu SQL-Mapping mit über 800 Objekten. Da es die Zeit vor den Generics in Delphi war, generierte ich einen typsicheren Containertyp für jedes Objekt sowie für andere Hilfs- und Konverterobjekte/-routinen.

Es ist eine Menge Arbeit zu Setup, und nur ist es wert, wenn Sie ein Projekt mit wirklich vielen Objekten und Feldern (Hunderte, wenn nicht Tausende) haben und sicher sind, dass Sie es mit erheblichen pflegen müssen Mutationen für eine lange Zeit zu kommen.

+0

"In Ihrem Fall ist das jedoch schwierig, da nicht alle Ihre Objekte (tobjectlist) von einer eigenen Hierarchie abgeleitet sind, die virtuelle abstrakte Methoden zum Laden/Speichern enthalten." Hier kommen Schnittstellen ins Spiel. Serialisierung hat nichts mit Implementierungsvererbung zu tun. Lassen Sie alle Objekte ein 'ISerializable' implementieren und Sie können loslegen. – mghie

+0

Sicher, möglich. (Allerdings müssen Sie die IUnknown-Methoden selbst und möglicherweise mehrmals für unterschiedlich verwurzelte Hierarchien implementieren, da tobjectlist nicht von iner facedobject erbt). –

0

Sie müssen eine Möglichkeit finden, dieses Objekt als Zeichenfolge zu codieren, damit Sie beim Erstellen eines neuen Objekts mit dieser Zeichenfolge dasselbe Objekt erhalten. Dies wird Serialisierung genannt, und einige Sprachen bieten diese Funktionalität für Sie.

Zum Beispiel, wenn Sie dieses Objekt haben:

class serialize_me { 
private: 
    int a; 
    float b; 
public: 
    double c; 
} 

und a = 5, b = 3,2 und c = 67,5

Zeichenfolge könnte wie folgt aussehen:

a5b3.2c67 .5

Dann könnten Sie diese Zeichenfolge analysieren und allen Mitgliedern die entsprechenden Werte zuweisen.

Ich denke, es versteht sich von selbst, dass Strings einfach auf der Festplatte gespeichert werden können.

EDIT: Dies ist ein sehr einfaches Beispiel, aber ich denke, dass Sie das Konzept leicht genug verstehen können.

EDIT: Delphi specific serialization. Am Ende der Seite befindet sich eine Verknüpfung zu einer vollwertigen XML-Serialisierungsklasse.

+1

Was tun Sie, wenn Ihr Objekt andere Objekte enthält? – Ampere

+2

@Altar: Rekursion. –

+0

"Delphi-spezifische Serialisierung" ist nur auf öffentliche/veröffentlichte Eigenschaften beschränkt. – Ampere

0

Viele Möglichkeiten, dies zu tun, denke ich, in der Vergangenheit habe ich Inifiles für diesen Zweck verwendet, nützlich als TMemInifile ist es für Sie eine Menge Grunzen Arbeit im Speicher zu tun, bevor Sie auf die Festplatte speichern. Da Sie eine Hierarchie zum Speichern haben, sollten Sie in Betracht ziehen, etwas wie XML zu verwenden, aber ich habe dies nicht persönlich getan, kann daher nicht darauf hinweisen.

0

Wenn das Objekt von TPersistent abgeleitet ist, XML oder JSON Serialisierung ist einfach mit Open-Source-Bibliotheken zu tun:

Eine Möglichkeit, Versionierung einfacher zu machen, ist eine anti-corruption layer zwischen dem Domänenmodell und der Persistenzschicht. (möglicherweise unter Verwendung von Datenübertragungsobjekten, die sich nicht bei jeder Änderung des Domänenmodells ändern).

Für die automatische Versionierung finden Sie in diesem Artikel: Migrate Serialized Java Objects with XStream and XMT

XMT stellt Klasse VersionedDocument auf Version serialisiert XMLs und die Migration behandeln. Das gleiche Design könnte problemlos mit Delphi implementiert werden.

+1

OmniXML ist so begrenzt wie jede andere Serialisierungsmethode, die ich bisher gefunden habe. Sie sagen: "Anmerkung: Klasse sollte von TPersistent abgeleitet werden und Eigenschaften sollten veröffentlicht werden". Die Serialisierung ist keine echte Lösung, um ein GESAMTES Objekt auf der Festplatte zu speichern, sondern nur einige seiner Teile (die veröffentlichten Eigenschaften). – Ampere

+1

Serialisierung über veröffentlichte Eigenschaften funktioniert für die gesamte VCL; Es gibt keinen Grund, es nicht zu benutzen. Wenn Sie die Struktur Ihrer Objekte steuern können, sollten Sie in Erwägung ziehen, veröffentlichte Eigenschaften hinzuzufügen, um zu zeigen, was Sie serialisieren müssen. Die veröffentlichten Eigenschaften müssen nicht von denselben Typen wie die zugrunde liegenden privaten Elemente sein, z. Sie haben ein privates TDateTime-Feld, legen es jedoch als String über eine veröffentlichte Prop an. Wenn nicht, könnten Sie jeder Objektklasse eine Lese-/Schreib-Eigenschaft .AsString hinzufügen, so dass jedes Objekt seinen eigenen Status liest und schreibt. Wie auch immer, du musst * etwas * tun. –

+0

@Altair + 1 Sie haben Recht, die Serialisierung sollte alle Felder des Objekts unabhängig von der Sichtbarkeit enthalten (weil dies der Zweck der Serialisierung ist: den vollständigen Objektstatus speichern). Delphi wurde in der Vergangenheit auf veröffentlichte und (mit $ M) öffentliche Felder beschränkt. Hoffentlich werden die Bibliotheken mit der neuen erweiterten RTTI diese Lücke schließen. – mjn

4

Wenn Sie Delphi 2010 verwenden, werden die Dinge dank der neuen RTTI-Einheit viel einfacher, Robert Love hat eine nette Einheit zum Serialisieren der Objekte in XML namens XMLSerial geschrieben.

Sie können darüber in seinem Blog lesen: Xml Serialization - Basic Usage

+0

Dies ist so begrenzt wie jede andere Serialisierungsmethode, die ich bisher gefunden habe, weil Eigenschaften veröffentlicht werden sollten. Die Serialisierung ist keine echte Lösung, um ein GESAMTES Objekt auf der Festplatte zu speichern, sondern nur einige seiner Teile (die veröffentlichten Eigenschaften). – Ampere

+0

Haben Sie tatsächlich den D2010-Winkel überprüft ?, da sich die RTTI-Generation dort geändert hat !!! –

+0

@Mohammad auch jvAppXMLFileStorage kann helfen, aber nur für veröffentlichte Felder. @Marco in Delphi2010 auch GetPropList nur die veröffentlichten Eigenschaften zurückgeben. –

2

Sie sagen, dass Serialisierung keine Lösung ist, aber ich frage warum nicht? Ich habe so etwas in der Vergangenheit gemacht, aber hier ist, was ich getan habe.

Ich habe eine Komponentenklasse erstellt, die nur ein nicht TPersistant-basiertes Objekt serialisiert, sodass ich es mithilfe von VCL-Streaming-Funktionen ein- und ausstrahlen kann.

Zum Beispiel:

// Bitte vergib mir für die Fehler, die es gibt, wie ich versuche, dies von meinem Kopf von oben eingeben. Dies wird auch nicht funktional vollständig sein.

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; 

Mit dieser Methode können Sie beliebige Daten aus der VCL oder benutzerdefinierten Daten streamen (serialisieren). Es braucht ein wenig, um einzurichten, aber die Stärke ist, dass Sie alles in und aus der Datendatei steuern können. Sie können sogar mit einem kleinen Vorüberdenken ein Versions-Flag erstellen und verschiedene Daten verarbeiten, indem Sie bestimmte Daten in neueren Versionen des Programms/der Komponente ignorieren oder massieren.

Sie können sogar andere VCL-Objekte aus Ihrer Streaming-Komponente streamen, solange Sie den Objekttyp (dh TComponent/TPersistant-basierte Objekte) bereits mit den vorhandenen Methoden des TReader/TWriter kennen.

Nicht eine vollständige Lösung, aber es sollte Sie dorthin bringen, wo Sie mit ein wenig mehr Arbeit gehen wollen.

+1

> Sie sagen, dass Serialisierung keine Lösung ist, aber ich frage warum nicht? ------
Bis jetzt konnten alle Streaming-Lösungen, die ich gefunden habe, NUR veröffentlichte Eigenschaften speichern. Einige Leute haben Probleme zu verstehen, dass das Speichern einiger der Attribute eines Objekts nicht dasselbe ist wie das Speichern des gesamten Objekts (also später können Sie den ursprünglichen Zustand wiederherstellen) :) – Ampere

+0

Obwohl ich nicht die gesamte Lösung zur Verfügung gestellt habe, verwenden Sie dies Methode ermöglicht es Ihnen, alles zu speichern, was Sie möchten. Ich denke, dass die meisten anderen Antworten auf dasselbe zielen, aber unterschiedliche Antworten auf das gleiche Problem gehören zu den großartigen Dingen beim Programmieren. Ich liebe dieses Hobby^H^H^H^H^H Job. –

+0

> "Obwohl ich nicht die gesamte Lösung bereitgestellt habe, können Sie mit dieser Methode alles speichern, was Sie möchten." --- Ja, ich weiß. Ich habe deinen Code gesehen. Deshalb habe ich "Bis jetzt" benutzt. Ich gewichte immer noch Ihre Lösung und die von GrandmasterB vorgeschlagene. – Ampere

2

Ihre aktuelle Lösung ist wahrscheinlich die beste, wenn Sie im Laufe der Jahre separate Versionen des Objekts haben.

Was ich mache, ist SaveToStream() und LoadFromStream() -Methoden zu erstellen und die Eigenschaften des Objekts manuell in einer festen Reihenfolge in einen tstream zu schreiben, wobei ihm eine Versionsnummer der Struktur vorangestellt wird. Der Vorteil dabei ist, dass Sie sich besser an ältere Versionen des Streams anpassen können. Wenn Sie z. B. 5 Versionen haben, aber für Dateien der Version 3 etwas auf eine bestimmte Weise initialisieren müssen, ist dies problemlos möglich. Sie umschließen dann eine SaveToFile() - Funktion, die einen TFileStream erstellt und SaveToStream() aufruft.

Ich glaube, es gibt eine TWriter-Klasse, mit der Sie verschiedene Datentypen einfacher in den Stream schreiben können ... oder Sie können einfach Ihre eigenen erstellen. (Ich habe meine eigenen Filestream-Abkömmling, um dies zu handhaben)

Wenn Sie mehrere Objekte in einem einzigen Strom speichern, möchten Sie möglicherweise die Position notieren, bevor jedes Objekt geschrieben wird, und dann zurückgehen und eine Länge markieren, so dass Sie oder jemand, der auf die Dateien zugreift) kann die Datei überspringen, ohne sie zu lesen.

Wenn Sie eine Hierarchie von Klassen haben, die Sie speichern möchten, laden Sie die Klasse "bottom load" mit allen Eigenschaften, die Sie in einer Datei speichern möchten. Auf diese Weise benötigen Sie nur eine Implementierung der Sicherungsroutine. Es ist ein wenig weniger effizient, da Sie Variablen herumtragen, die Sie nicht unbedingt in allen Objekten benötigen, aber es ist viel einfacher zu verwalten.

+0

Hallo Großmeister. Ich denke, ich werde auf diesem Weg weitermachen. Vielen Dank. – Ampere

1

Im Moment versuche ich jedes Objekt unabhängig als Binärdatei zu speichern, die dann zusammen verpackt werden.

Ich denke, das ist ein guter Ansatz. Es kann ein bisschen Codierung erfordern, aber es ist schnell! Außerdem können Sie es später problemlos aktualisieren - für den Fall, dass Sie Änderungen an Ihrem Dateiformat vornehmen müssen. Für ein einfaches Format-Upgrade vergessen Sie nicht, einige Füll-Bytes in Ihrer Datei zu belassen. Sie können später weitere Informationen hinzufügen, ohne das Dateiformat zu ändern.

Verwandte Themen