AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein XML Delphi Serialisierung von TCollection mit TXMLSerializer
Thema durchsuchen
Ansicht
Themen-Optionen

Serialisierung von TCollection mit TXMLSerializer

Ein Thema von Dawn87 · begonnen am 1. Jul 2009 · letzter Beitrag vom 13. Jul 2009
Antwort Antwort
Dawn87

Registriert seit: 15. Feb 2007
Ort: Lüdenscheid
189 Beiträge
 
Delphi XE5 Professional
 
#1

Serialisierung von TCollection mit TXMLSerializer

  Alt 1. Jul 2009, 15:29
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:
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;
Die Routine zum Speichern:
Delphi-Quellcode:
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;
Nun probiere ich diese Collection wieder aus der XML auszulesen also zu deserialisieren:

Delphi-Quellcode:
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;
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.

Hat jemand eine Idee wie ich das Problem lösen könnte?

Grüße
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.163 Beiträge
 
Delphi 12 Athens
 
#2

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 1. Jul 2009, 15:47
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
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Dawn87

Registriert seit: 15. Feb 2007
Ort: Lüdenscheid
189 Beiträge
 
Delphi XE5 Professional
 
#3

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 1. Jul 2009, 16:10
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;
  Mit Zitat antworten Zitat
Benutzerbild von mirage228
mirage228

Registriert seit: 23. Mär 2003
Ort: Münster
3.750 Beiträge
 
Delphi 2010 Professional
 
#4

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 1. Jul 2009, 16:14
Also in meinem XML Serializer werden Collections speziell beim Lesen/Schreiben behandelt.
Delphi-Quellcode:
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;
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.

Vielleicht hilft dir dieser Ansatz ja.

Viele Grüße
David F.

May the source be with you, stranger.
PHP Inspection Unit (Delphi-Unit zum Analysieren von PHP Code)
  Mit Zitat antworten Zitat
Benutzerbild von geskill
geskill

Registriert seit: 17. Feb 2007
Ort: NRW
420 Beiträge
 
Delphi 2010 Professional
 
#5

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 3. Jul 2009, 09:47
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;
Angehängte Dateien
Dateityp: pas uxmlserializer_420.pas (51,4 KB, 21x aufgerufen)
Sebastian
  Mit Zitat antworten Zitat
Benutzerbild von stoxx
stoxx

Registriert seit: 13. Aug 2003
1.111 Beiträge
 
#6

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 3. Jul 2009, 12:25
schonmal das probiert? würde es damit gehen?

http://www.delphipraxis.net/internal...&highlight=xml


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.
Phantasie ist etwas, was sich manche Leute gar nicht vorstellen können.
  Mit Zitat antworten Zitat
Benutzerbild von geskill
geskill

Registriert seit: 17. Feb 2007
Ort: NRW
420 Beiträge
 
Delphi 2010 Professional
 
#7

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 3. Jul 2009, 18:15
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 ;)
Sebastian
  Mit Zitat antworten Zitat
Dawn87

Registriert seit: 15. Feb 2007
Ort: Lüdenscheid
189 Beiträge
 
Delphi XE5 Professional
 
#8

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 8. Jul 2009, 10:09
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
  Mit Zitat antworten Zitat
Dawn87

Registriert seit: 15. Feb 2007
Ort: Lüdenscheid
189 Beiträge
 
Delphi XE5 Professional
 
#9

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 8. Jul 2009, 15:19
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?
  Mit Zitat antworten Zitat
Benutzerbild von geskill
geskill

Registriert seit: 17. Feb 2007
Ort: NRW
420 Beiträge
 
Delphi 2010 Professional
 
#10

Re: Serialisierung von TCollection mit TXMLSerializer

  Alt 13. Jul 2009, 13:10
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:
            //[...]
            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;
               { --------------------------- }
//[...]
Der Benutzer muss "nur" noch die TCollection's erstellen:
Delphi-Quellcode:
procedure TSettingsManager.SpecialClassLoad(aSender: TObject; aObjectName: string; var aObject: TPersistent);
begin
  if aObjectName = 'Conditionsthen
      aObject := TCollection.Create(TConditionsCollectionItem)
    else
      if aObjectName = 'SubConditionsthen
        aObject := TCollection.Create(TSubConditionsCollectionItem)
end;
Delphi-Quellcode:
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;
Man könnte das ganze theoretisch auch mit dem Event "OnStartObjectLoad" durchführen dieses benötigt dann aber wieder eine eingebundene xml Unit.
Angehängte Dateien
Dateityp: pas uxmlserializer_107.pas (52,2 KB, 24x aufgerufen)
Sebastian
  Mit Zitat antworten Zitat
Antwort Antwort


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 20:34 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