AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Generischer Serialisierer

Ein Thema von EConvertError · begonnen am 12. Feb 2006 · letzter Beitrag vom 26. Feb 2006
Antwort Antwort
Seite 1 von 2  1 2      
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#1

Generischer Serialisierer

  Alt 12. Feb 2006, 19:29
Guten Abend!

Mein Ziel ist es, Objekte (de)serialisieren zu können (zu XML-Dateien und TStreams). Ich weiß, es gibt das so Dinge wie die dpCollection, aber mich interessiert ein ganz anderer Ansatz:

Hier habe ich etwas von einem generischem Serialisierer gelesen.

Zitat:
Delphi-Quellcode:
type
  ISerializerStrategy = interface();
    function GetNameOfClass(aClass:TClass):string;
    function GetClassByName(const Name:string):TClass;
    ...
  end;

  TObjectSerializer = class
  protected
    procedure SerializeObject(aObject:TObject);
  public
    constructor create(stream:TStream; SerializerStrategy:ISerializerStrategy);
    procedure SerializeObjects(List:TObjectList);
  end;

...
// verwende:
var
  Serializer:TObjectSerializer;
...
  Serializer:=TObjectSerializer.Create(myStream, myStrategyImplementation);
  Serializer.SerializeObjects(myObjectList);
Leider kann ich mit diesem Hinweis noch nicht genug anfangen. Hoffentlich könnt ihr mir da weiterhelfen!

Ich habe mir das so vorgestellt, dass ich eine Schnittstelle ISerializable definiere, die von allen Objekten die serialisiert werden wollen implementiert werden muss, und eine Schnittstelle ISerializer, die von allen Serialisierern (XmlSerialisierer, BinärSerialisierer) implementiert werden muss.

Das Problem: Wie serialisiere ich ein Objekt und wie deserialisiere ich ein Objekt? Wie soll das ganze funktionieren? Wie kann ich zur Laufzeit ein Objekt erstellen, von dem ich nur den Klassennamen als String weiß (beim Deserialisieren)?

Vielen Dank,
Andreas
Andreas N.
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#2

Re: Generischer Serialisierer

  Alt 12. Feb 2006, 20:00
Zitat:
Das Problem: Wie serialisiere ich ein Objekt und wie deserialisiere ich ein Objekt? Wie soll das ganze funktionieren? Wie kann ich zur Laufzeit ein Objekt erstellen, von dem ich nur den Klassennamen als String weiß (beim Deserialisieren)?
TReader macht es so:
Result := TComponentClass(FindClass(ReadStr)).Create(nil); Das zeigt aber, dass du jede Klasse zum (De)serialisieren erst registrieren musst. Auch sonst wirst du schnell an die Grenzen der RTTI stoßen, der Aufwand ist IMHO nicht gerechtfertigt. Für einen wirklichen Serializer benötigst du etwas Größeres als die RTTI, wie z.B. Reflection von .NET. Und wie der Zufall es will, sieht das Serializing der FCL dem obigen Code verdammt ähnlich aus .
[OT]
Zitat von Taladan:
Vielleicht hilft dir das weiter.

Objecktliste mit polomorten Objecten speichern

[/OT]
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#3

Re: Generischer Serialisierer

  Alt 12. Feb 2006, 20:29
Danke für die Antwort!

Ich kenne die Serialisierung von .NET (bin auch C#-Programmierer). Deshalb ist die Ähnlichkeit der Quellcodes kein Zufall!
Mir ist auch klar, dass ich in Delphi (mangels Runtime) nie so etwas wie die .NET Reflection haben werde.

Aber der Ansatz von Maximov mit dem generischen Serialisierer interessiert mich sehr. Leider ist mir da noch zu wenig Information dabei, und ich würde gerne wissen, wie das ganz genau funktionieren soll.

Meine Idee:
Wenn ich im ISerializable Interface die Funktion "GetObjectData" habe, bekomme ich schon mal die zu serialisierenden Daten, die ich dann mit dem ISerializable Interface schreiben kann.
Beim deserialisieren könnte ich ja mit dem Klassennamen eine Instanz erzeugen und wieder mit einer Funktion "WriteObjectData" des ISerializable Interfaces die ganzen Daten schreiben.

Genau das Erstellen eines Objektes basierend am Klassennamen habe ich noch nicht geschafft. Mein Ansatz:
Delphi-Quellcode:
var
  Obj : TObject;
  ClassName: String;
begin
  ...
  Obj := GetClass(ClassName).Create;
  if Obj is TButton then
    // Hier tritt dann eine Access-Violation auf:
    TButton(obj).Parent := self; //self = TForm
Interessant wäre hierbei auch noch, ob ich da ganz etwas anderes vorhabe, wie Maximov es vorgeschlagen hat, also ob ich seine Idee komplett missverstanden habe.

EDIT: Diese Lösung (TObjectList mit polimorphen Objecten zum Speichern) gefällt mir nicht, weil ich die zu speichernden Eigenschaften als published deklarieren muss. Ich möchte aber auch private-Felder speichern.

Danke,
Andreas
Andreas N.
  Mit Zitat antworten Zitat
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#4

Re: Generischer Serialisierer

  Alt 13. Feb 2006, 20:35
Obwohl ich, was Maximovs Ansatz betrifft, immer noch anstehe, habe ich mich weitergebildet und mir etwas überlegt:
Das ISerializable Interface in .net verlangt die Funktion "GetObjectData", mit der man sich die zu serialisierenden Daten holt. So etwas muss in Win32 auch möglich sein.
Allerdings verlangt die Schnittstelle in .net auch einen Konstruktor mit den gleichen Argumenten, um das Objekt wieder deserialisieren zu können. Hier wird die ganze Geschichte für mich problematisch, da ich ja nicht schreiben kann:
Delphi-Quellcode:
type
  ITestInterface = Interface(IInterface)
    constructor Create({My arguments});
  end;
Das zweite größere Problem ist: Wie erzeuge ich ein Objekt vom benötigten Typ, wenn ich nur die Eigenschaft ClassType oder ClassName des Objekts gegeben habe? Geht das überhaupt? Ich bräuchte das zum deserialisieren...

Vielen Dank,
Andreas
Andreas N.
  Mit Zitat antworten Zitat
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#5

Re: Generischer Serialisierer

  Alt 18. Feb 2006, 11:21
*push*
Andreas N.
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#6

Re: Generischer Serialisierer

  Alt 18. Feb 2006, 13:15
Zitat von EConvertError:
Danke für die Antwort!

Ich kenne die Serialisierung von .NET (bin auch C#-Programmierer). Deshalb ist die Ähnlichkeit der Quellcodes kein Zufall!
Warum tust du dir dann die RTTI überhaupt noch an ? Gut, einen Versuch ist es wert, aber IMHO lohnt sich der Aufwand nicht, in der Zeit spiele ich lieber mit Reflection rum .
Zitat:
Genau das Erstellen eines Objektes basierend am Klassennamen habe ich noch nicht geschafft. Mein Ansatz:
Delphi-Quellcode:
var
  Obj : TObject;
  ClassName: String;
begin
  ...
  Obj := GetClass(ClassName).Create;
  if Obj is TButton then
    // Hier tritt dann eine Access-Violation auf:
    TButton(obj).Parent := self; //self = TForm
Du musst den richtigen Konstruktor aufrufen:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  ClassType: TClass;
  Instance: TControl;
begin
  RegisterClass(TButton);

  ClassType := GetClass('TButton');
  if ClassType = nil then
    ShowMessage('Klasse nicht gefunden')
  else
    if ClassType.InheritsFrom(TControl) then
    begin
      Instance := TControlClass(ClassType).Create(Self);
      if Instance = nil then
        ShowMessage('Instanz nicht erstellt')
      else
        Instance.Parent := Self;
    end
    else
      ShowMessage('Klasse kein Control');
end;
Zitat:
Interessant wäre hierbei auch noch, ob ich da ganz etwas anderes vorhabe, wie Maximov es vorgeschlagen hat, also ob ich seine Idee komplett missverstanden habe.
Ich bin zwar nicht Maximov, aber ich denke nicht, dass du ihn falsch verstanden hast. Eben soetwas, wie man es aus der FCL kennt.

Zitat:
Diese Lösung (TObjectList mit polimorphen Objecten zum Speichern) gefällt mir nicht, weil ich die zu speichernden Eigenschaften als published deklarieren muss. Ich möchte aber auch private-Felder speichern.
Das war nicht zur Anregung gedacht (sonst könntest du ja einfach TReader nehmen), ich fand nur den Schreibfehler lustig .
Zitat von EConvertError:
Allerdings verlangt die Schnittstelle in .net auch einen Konstruktor mit den gleichen Argumenten, um das Objekt wieder deserialisieren zu können. Hier wird die ganze Geschichte für mich problematisch, da ich ja nicht schreiben kann:
Delphi-Quellcode:
type
  ITestInterface = Interface(IInterface)
    constructor Create({My arguments});
  end;
Ohne Reflection gibt es wohl nur einen Weg: Eine Basisklasse statt einem Interface:
Delphi-Quellcode:
TSerializable = class
public
  constructor Create (info: TSerializationInfo, ...); abstract;
Zitat:
Das zweite größere Problem ist: Wie erzeuge ich ein Objekt vom benötigten Typ, wenn ich nur die Eigenschaft ClassType oder ClassName des Objekts gegeben habe? Geht das überhaupt? Ich bräuchte das zum deserialisieren...
Hast du den passenden Code nicht schon oben geschrieben (und ich verbessert)?
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#7

Re: Generischer Serialisierer

  Alt 19. Feb 2006, 09:58
Danke für die Antwort!

Zitat:
Warum tust du dir dann die RTTI überhaupt noch an?
Ganz einfach: Delphi für Win32, C# für .net. Wenn ich also an einem Win32-Programm arbeite, brauche ich Delphi.

Danke für deine Vorschläge! Basisklasse ist für mich auch kein Problem.

In deinem Code sehe ich ein ganz anderes Problem:
Du registrierst eine Klasse (RegisterClass(TButton)), um dann später selbige mit GetClass() wieder zu holen. Ich weiß aber nicht welche Klasse es ist (außer, dass sie von meiner Basisklasse abgeleitet ist), weshalb ich sie vorher nicht registrieren kann (und deshalb kann ich sie mir per GetClass auch nicht holen).

In meiner XML-Datei steht die Klasse ja nur als String drinnen...

Vermutlich läuft das ganze darauf hinaus, dass ich beim Deserialisieren den Klassentyp übergeben muss und somit schon wissen muss, was ich deserialisiere.

Was meint ihr?

Vielen Dank,
Andreas
Andreas N.
  Mit Zitat antworten Zitat
Benutzerbild von Khabarakh
Khabarakh

Registriert seit: 18. Aug 2004
Ort: Brackenheim VS08 Pro
2.876 Beiträge
 
#8

Re: Generischer Serialisierer

  Alt 19. Feb 2006, 11:16
Das Problem hatte ich ja auch schon angesprochen. IMO gibt es nur zwei Möglichkeiten: Entweder man übergibt dem Deserializer den genauen Klassentyp, wie du schon geschrieben hast. Ich kann mir aber gut vorstellen, dass man diesen oft gar nicht / nur eine Basisklasse kennt. Dann gibt es nur noch einen Weg:
Der Deserializer muss einfach annehmen können, dass jede einzelne serialisierbare Klasse schon registriert wurde, z.B. im initialization-Abschnitt der Unit. Bei jeder Ableitung muss der Programmierer also ein RegisterClass(...) hinzufügen.
Sebastian
Moderator in der EE
  Mit Zitat antworten Zitat
Benutzerbild von maximov
maximov

Registriert seit: 2. Okt 2003
Ort: Hamburg
548 Beiträge
 
Delphi 2005 Professional
 
#9

Re: Generischer Serialisierer

  Alt 20. Feb 2006, 10:50
Zitat von Khabarakh:
...
Dann gibt es nur noch einen Weg:
Der Deserializer muss einfach annehmen können, dass jede einzelne serialisierbare Klasse schon registriert wurde, z.B. im initialization-Abschnitt der Unit. Bei jeder Ableitung muss der Programmierer also ein RegisterClass(...) hinzufügen.
Genauso sehe ich das auch! Es sei denn du willst klassen ermitteln, ohne sie vorher explizit zu registrieren, dann könnte dieser thread sehr hilfreich sein: http://www.delphipraxis.net/internal...ght=hagen+rtti
...dazu musst du deine klassen aber in packages unterbringen, da der compiler sonst entfernen würde, weil er glaubt sie würden nicht verwendet.


Mein generischer serialisierer ist eine unfertige idee gewesen, sollte aber mit ein bischen hirnschalz zu bewältigen sein. Ich versuch später mir was zu überlegen, bzw. das ein bischen auszuführen
mâxîmôv.

{KDT}
  Mit Zitat antworten Zitat
EConvertError

Registriert seit: 29. Sep 2003
Ort: Österreich
230 Beiträge
 
#10

Re: Generischer Serialisierer

  Alt 20. Feb 2006, 15:30
Danke für eure Antworten!

Wie du, Khabarakh, schon geschrieben hast, ist das ganze etwas viel Aufwand, nur um ein paar Objekte zu speichern.

Ich denke, dass ich es anders machen werde:
Eine TSerializableObject-Basisklasse mit...
  • ...dem Konstruktor Create(var TDeserializer), mit dessen Hilfe ich dann Alles deserialisieren kann...
  • ...und einer Prozedur Serialize(var TSerializer), mit der ich serialisiere.

In diesem Fall hätte ich das (De)Serialisieren erledigt, ohne dass ich diese Probleme habe.

Der Grundgedanke des generischen Serialisierers ist mir schon klar, aber an der Ausführung happert es noch ein wenig. Ich wäre froh, wenn du, Maximov, mal ein kleines Beispiel posten oder in die Codelibrary stellen könntest.

Vielen Dank,
Andreas
Andreas N.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:18 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz