![]() |
Serialisierung von TCollection mit TXMLSerializer
Hallo,
ich benutze den XMLSerializer von dragonsoft und hab nun leider damit ein Problem: Ich habe von TCollection und TCollectionItem abgeleitete Klassen die ich serialisiere und als XML-Datei speicher. Das klappt soweit auch ganz gut: Die Klassen:
Delphi-Quellcode:
Die Routine zum Speichern:
type
TStorageItem = class(TCollectionItem) private FText: String; function GetM_sText: string; procedure SetM_sText(Value: string); published property Text: String read GetM_sText write SetM_sText; end; type TStorage = class(TCollection) private function GetItem(Index: Integer): TStorageItem; procedure SetItem(Index: Integer; const Value: TStorageItem); public function Add: TStorageItem; function FindItemID(ID: Integer): TStorageItem; function Insert(Index: Integer): TStorageItem; property Items[Index: Integer]: TStorageItem read GetItem write SetItem; end;
Delphi-Quellcode:
Nun probiere ich diese Collection wieder aus der XML auszulesen also zu deserialisieren:
procedure TForm1.Button1Click(Sender: TObject);
var coTest: TStorage; coStore: TStorageItem; begin coTest := TStorage.Create(TStorageItem); coStore := coTest.Add; coStore.Text := 'Test'; coStore := coTest.Add; coStore.Text := 'Test2'; coStore := coTest.Add; coStore.Text := 'Test3'; with TXMLSerializer.Create(self) do begin XMLSettings.WellFormated := true; StorageOptions := [soIncludeObjectLinks, soSortProperties]; SpecialClasses := [scTCollection]; SaveObject(coTest, 'Test'); SaveToFile('C:\test.xml'); end; end;
Delphi-Quellcode:
Das ganze Funktioniert nicht (coTest.Count ist immer 0). Ich habe mir den Quellcode der Deserialisierungsroutinen mal angeschaut und diese versucht per SetPropValue() die Count-Eigenschaft von meiner TStorage-Klasse zu manipulieren. Da diese aber Read-only ist, klappt das nicht.
var
coTest: TStorage; coStore: TStorageItem; i: Integer; begin coTest := TStorage.Create(TStorageItem); with TXMLSerializer.Create(self) do begin XMLSettings.WellFormated := true; SpecialClasses := [scTCollection]; LoadFromFile('C:\test.xml'); LoadObject(coTest, 'Test'); end; ListBox1.Clear; for i := 0 to coTest.Count - 1 do begin ListBox1.AddItem(coTest.Items[i].Text, nil); end; Hat jemand eine Idee wie ich das Problem lösen könnte? Grüße |
Re: Serialisierung von TCollection mit TXMLSerializer
Tja, wie viele andere Serialisierer erstellt dieser auch keine Objekt-Instanzen ... ich bin selber bei meinem eigenem XMLSerialierer noch nicht wirklich zu einer guten Lösung gekommen, aber im Endefekt wird vermutlich mal eine Callback-Funktion aufgerufen, wo DU dann selber die Klasse erstellen müßtest, wenn sie noch nicht existiert.
[add] theoretisch könntest du erstmal die Collection selber mit den nötigen Klassen füllen und dann mal schauen, ob die Deserialisierung dann diese Klassen mit Inhalt befüllt |
Re: Serialisierung von TCollection mit TXMLSerializer
Hallo!
Das war die entscheidene Information die mir fehlte: Die von mir verwendete Klasse hat natürlich Events! Habe das ganze nun erstmal provisorisch zum laufen bekommen und beschäfte mich gleich noch ein wenig damit:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var coStore: TStorageItem; i: Integer; begin coTest := TStorage.Create(TStorageItem); with TXMLSerializer.Create(self) do begin XMLSettings.WellFormated := true; SpecialClasses := [scTCollection]; LoadFromFile('C:\test.xml'); OnStartObjectLoad := Deserialize; LoadObject(coTest, 'Test'); end; ListBox1.Clear; for i := 0 to coTest.Count - 1 do begin ListBox1.AddItem(coTest.Items[i].Text, nil); end; end;
Delphi-Quellcode:
procedure TForm1.Deserialize(aSender, aObject: TObject; aObjectName: string;
aNode: IXMLNode; var aSkipObject: boolean); var i: Integer; begin for i := 0 to aNode.ChildNodes.Nodes['customdata'].ChildNodes.count - 1 do begin coTest.Add; end; end; |
Re: Serialisierung von TCollection mit TXMLSerializer
Also in meinem XML Serializer werden Collections speziell beim Lesen/Schreiben behandelt.
Delphi-Quellcode:
Und beim Lesen/Schreiben einer Klasse wird auch geprüft, um es sich um TCollection oder eine Nachfahren handelt und dann halt die Objekte via Iteration über diese Liste erzeugt bzw. geschrieben. Lediglich die TCollection Klasse selbst muss erzeugt sein.
procedure TXmlSerializer.WriteCollection(AObject: TCollection;
const Node: IXMLNode); var I: Integer; begin if (Node <> nil) and (AObject <> nil) then for i := 0 to AObject.Count - 1 do WriteClass(AObject.Items[i], Node.AddChild('item')); end; procedure TXmlSerializer.ReadCollection(AObject: TCollection; const Node: IXMLNode); var I: Integer; Item: TPersistent; begin if (Node <> nil) and (AObject <> nil) then for i := 0 to Node.ChildNodes.Count - 1 do begin Item := AObject.Add(); ReadClass(Item, Node.ChildNodes[i]); end; end; Vielleicht hilft dir dieser Ansatz ja. Viele Grüße |
Re: Serialisierung von TCollection mit TXMLSerializer
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe die Klasse mir jetzt so umgeschrieben, damit dort ohne Probleme TCollectionItem's gespeichert und geladen werden können. Außerdem benutze ich nicht den MSXML Parser sondern den aus dem alcinoe Projekt.
Dawn87, wenn ich richtig gelesen habe, benutzt du den XMLSerializer von DragonSoft, diesen habe ich modifiziert. Dadruch spart man sich auf das Event zu reagieren. In der Funktion LoadSingleClass:
Delphi-Quellcode:
//[...]
if fSpecialClasses<>[] then begin if (scTCollection in fSpecialClasses) and aPropertyInstance.InheritsFrom(TCollection) then begin lPropNodes := aNode.ChildNodes.FindNode('customdata'); { --------------------------- } for I := 0 to lPropNodes.ChildNodes.Count - 1 do if (lPropNodes.ChildNodes.Nodes[I].NodeName = 'class') then TCollection(aPropertyInstance).Add; { --------------------------- } if Assigned(lPropNodes) then begin for i := 0 to TCollection(aPropertyInstance).Count -1 do LoadClass(lPropNodes, TCollection(aPropertyInstance).Items[i], 'Item_' + IntToStr(i)); end; end //[...]
Delphi-Quellcode:
procedure TSettingsManager.LoadSettings;
var XMLSerializer:TXMLSerializer; procedure LoadObjectEx(const aInstance: TObject); begin XMLSerializer.LoadObject(aInstance,aInstance.ClassName); end; begin if FileExists(GetSettingsFolder + SettingsFilename) then begin XMLSerializer := TXMLSerializer.Create(nil); try with XMLSerializer do begin XMLSettings.WellFormated := true; SpecialClasses := [scTCollection]; LoadFromFile(GetSettingsFolder + SettingsFilename); LoadObjectEx(SettingsManager.Settings); // zu ladende Klasse end; finally XMLSerializer.Free; end; end else LoadDefaultSettings; end;
Delphi-Quellcode:
procedure TSettingsManager.SaveSettings;
var XMLSerializer:TXMLSerializer; procedure SaveObjectEx(const aInstance: TObject); begin XMLSerializer.SaveObject(aInstance,aInstance.ClassName); end; begin XMLSerializer := TXMLSerializer.Create(nil); try with XMLSerializer do begin XMLSettings.WellFormated := true; StorageOptions := [soIncludeObjectLinks, soSortProperties]; SpecialClasses := [scTCollection]; SaveObjectEx(SettingsManager.Settings); // zu speichernde Klasse SaveToFile(GetSettingsFolder + SettingsFilename); end; finally XMLSerializer.Free; end; end; |
Re: Serialisierung von TCollection mit TXMLSerializer
schonmal das probiert? würde es damit gehen?
![]() ich hatte da einige Anpassungen vorgenommen, dass auch abgeleitete Klassen gehen, irgendwo muss glaub ich auch anstatt eines Exit ein Continue. Bin mir aber nich mehr so sicher, müsste ich nochmal reinschauen. |
Re: Serialisierung von TCollection mit TXMLSerializer
Ich habe es zuerst mit deiner Modifikation versucht. Leider hat das vorne und hinten nicht gepasst, lauter AVs etc.
Wenn man die alcinoe XML nutzen will muss man einige Sachen mehr beachten, die man bei MSXML nicht unbedingt beachten muss. Aber es klappt ja jetzt so wie ich es will ;) |
Re: Serialisierung von TCollection mit TXMLSerializer
Hallo,
sorry für die späte Antwort: Stimmt! Es ist der DragonSoft-Serialisierer. Deine Überarbeitung sieht sehr vielversprechend aus und macht die Benutzung der Komponente wesentlich schöner. Vielen Dank an alle! Grüße |
Re: Serialisierung von TCollection mit TXMLSerializer
Hab das ganze nun mehr oder weniger unter Delphi 2009 zum laufen gebracht hab aber das Problem das ich u.A. ein recht komplexes Objekt serialisieren und deserialisieren will.
Dieses Objekt hält Referenzen zu anderen Objekte, die wiederum Referenzen zu weiteren Objekte haben. Das ganze ist doch nicht so einfach wie ich es mir erhofft hatte. Die Serialisierung mit .NET scheint da wohl ein Segen zu sein? |
Re: Serialisierung von TCollection mit TXMLSerializer
Liste der Anhänge anzeigen (Anzahl: 1)
Dawn87 und ich hatten nun noch ein Problem, was passiert wenn man in einem TCollectionItem eine weitere TCollection einbindet, klar der Serializer wird diese nicht laden, da die Collection niemals erstellt wurde.
Um das Problem zu lösen habe ich ein neues Event erstellt und rufe dies auf, bevor die Klasse geladen wird:
Delphi-Quellcode:
Der Benutzer muss "nur" noch die TCollection's erstellen:
//[...]
if ANSISameText(lPropNode.Attributes['type'], 'Object') then begin lPropObj := TPersistent(GetObjectProp(aPropertyInstance, lPropName)); if Assigned(lPropObj) then LoadClass(lPropNode, lPropObj, lPropName) { --------------------------- } else if ANSISameText(lPropNode.Attributes['class'],'TCollection') then begin fOnSpecialClassLoad(self,lPropName,lPropObj); if not Assigned(lPropObj) then raise Exception.Create('Object of '+ lPropName +' is undefined'); SetObjectProp(aPropertyInstance,lPropName,lPropObj); LoadClass(lPropNode, lPropObj, lPropName); end; { --------------------------- } //[...]
Delphi-Quellcode:
procedure TSettingsManager.SpecialClassLoad(aSender: TObject; aObjectName: string; var aObject: TPersistent);
begin if aObjectName = 'Conditions' then aObject := TCollection.Create(TConditionsCollectionItem) else if aObjectName = 'SubConditions' then aObject := TCollection.Create(TSubConditionsCollectionItem) end;
Delphi-Quellcode:
Man könnte das ganze theoretisch auch mit dem Event "OnStartObjectLoad" durchführen dieses benötigt dann aber wieder eine eingebundene xml Unit.
type
TMyCollectionItem = class(TCollectionItem) private FConditions:TCollection; published property Conditions:TCollection read FConditions write FConditions; end; TConditionsCollectionItem = class(TCollectionItem) private FSubConditions:TCollection; published property SubConditions:TCollection read FSubConditions write FSubConditions; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:33 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz