Laden...

XML-Serialisieren/Deserialisieren für IsolatedStorage verdoppelt items in einer Liste

Erstellt von UHM vor 2 Jahren Letzter Beitrag vor 2 Jahren 228 Views
U
UHM Themenstarter:in
22 Beiträge seit 2009
vor 2 Jahren
XML-Serialisieren/Deserialisieren für IsolatedStorage verdoppelt items in einer Liste

Hallo,

ich habe eine abgeleitete serialisierbare Klasse zum Speichern der Benutzterdaten im Isolated Storage. Dabei ist eine Liste mit Dateinamen in der Basisklasse, die in einer DLL ist. Leider verdoppelt sich die Liste jedes Mal beim serialisieren und wieder deserialisieren. Auszüge aus der Klasse sind dabei:


  [Serializable]
  public class UserData
  {
    public List<String> strlOtherFilename;

    public List<String> OtherFilenames
    {
      get { return strlOtherFilename; }
    }

    public UserData()
    {
      strlOtherFilename = new List<string>();
      ....
    }
  }

  [Serializable]
  public class AppUserData:UserData
  {
    // hier ist in der ganzen abgeleiteten Klasse nichts mit den Dateinamen
    ...
    public AppUserData()
    {
       ...
    }
  }

  public partial class DrawingApp : Form, IMessageFilter
  {
    protected UserData Benutzerdaten;

   ...

    virtual protected void AppSaveUserData(XmlSerializer xs, IsolatedStorageFileStream userData)
    {
      if (null == xs)
      {
        xs = new XmlSerializer(typeof(UserData));
      }
      xs.Serialize(userData, Benutzerdaten);
    }

    virtual protected void AppLoadUserData(XmlSerializer xs, StreamReader sr)
    {
      if (null == xs)
      {
        xs = new XmlSerializer(typeof(UserData));
      }
      Benutzerdaten = (UserData)xs.Deserialize(sr);
    }

    virtual protected void DrawingApp_Load(object sender, EventArgs e)
    {
      try
      {
        using (IsolatedStorageFileStream userData = new IsolatedStorageFileStream(GetUserDataStorageName(), FileMode.Open))
        {
          if (null != userData)
          {
            using (StreamReader sr = new StreamReader(userData))
            {
              AppLoadUserData(null, sr);
            }
            ...
          }
        }
      }
      catch
      {
        Benutzerdaten = new UserData();
      }
      ...
  }

    override protected void AppLoadUserData(XmlSerializer xs, StreamReader sr)
    {
      if (null == xs)
      {
        xs = new XmlSerializer(typeof(AppUserData));
      }
      audBenutzerdaten = (AppUserData)xs.Deserialize(sr);
      SetUserData(audBenutzerdaten);

    }

  public partial class MainApp : DLL.DrawingApp
  {
    protected AppUserData audBenutzerdaten;

    ...

    override protected void AppSaveUserData(XmlSerializer xs, IsolatedStorageFileStream userData)
    {
      ...
      if (null == xs)
      {
        xs = new XmlSerializer(typeof(AppUserData));
      }
      FileStream fs = new FileStream("Test.txt", FileMode.Create);
      xs.Serialize(fs, (AppUserData)audBenutzerdaten);   // Testausgabe in Datei
      xs.Serialize(userData, (AppUserData)audBenutzerdaten);
   }

    override protected void DrawingApp_Load(object sender, EventArgs e)  // Event Form.Load
    {
      base.DrawingApp_Load(sender, e);
      ...
    }

Benutzerdaten und audBenutzerdaten sind zwar auf dem ersten Blick 2 verschiedene Objekte, werden aber immer gleichgesetzt gehalten (Benutzerdaten = audBenutzerdaten).
In der zum Test ausgegebenen Datei, die wie ich hoffe dann identisch mit dem Isolated Storage ist, steht dann:


<?xml version="1.0"?>
<RvmConfigUserData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <strlOtherFilename>
    <string>C:\...\Test.tst</string>
  </strlOtherFilename>
  ...
  <OtherFilenames>
    <string>C:\...\Test.tst</string>
  </OtherFilenames>
  ...
</AppUserData>

Für mich ist dabei etwas verwunderlich, daß dann da <OtherFilenames> mit enthalten ist, das ist ja nur ein Zugriffskonstrukt. Sollte aber eigentlich nichts bewirken, da <OtherFilenames> ja nur get besitzt und kein set.
Oder interpretiere ich da etwas falsch?

Gruß
UHM

16.806 Beiträge seit 2008
vor 2 Jahren

  public List<String> strlOtherFilename;

  public List<String> OtherFilenames
  {
    get { return strlOtherFilename; }
  }

Damit hast Du eine Eigenschaft mit einem expliziten Feld erzeugt. Aber:

  • Felder sollten (ausser in ein paar Ausnahmen) immer private sein
  • Das Feld ist unnötig, Dir reicht ein get mit einem hidden field => public List<String> OtherFilenames {get;}

Dadurch, dass Du das Feld public hast, beachtet der Serializer das Feld beim Serialisieren.
Das ist Dein Fehler, und durch das doppelte Serialisieren des Inhalts haste Dein Ergebnis.
Siehe dazu die Basics in https://docs.microsoft.com/de-de/dotnet/api/system.xml.serialization.xmlserializer?view=net-5.0

Ansonsten noch Feedback an den Code:

  • Du disposed die Handles nicht, zB bei den Streams -> kann zu Memory Leaks / Memory Exceptions führen
  • Kannst Dir die ganze Arbeit fast vollständig sparen, wenn Du System.Text.Json verwendest, das zudem viel performanter und schlanker ist.
T
2.219 Beiträge seit 2008
vor 2 Jahren

Ist ein einfacher Fehler.
strlOtherFilename ist als public deklariert, entsprechend wird die Liste einmal durch das public Feld und durch OtherFilenames serailisiert.
In deinem Fall sollte auch eine einfache Auto Property reichen, dann hast du den Fehler nicht.

Nachtrag:
Abt war schneller 🙂
Falls möglich steig auf Json um.
Wenn ihr größere Listen/Objekte fahrt, wird Xml schnell zum Overhead.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

U
UHM Themenstarter:in
22 Beiträge seit 2009
vor 2 Jahren

Läuft, besten Dank und schönen Abend noch. 🙂