Einführung

Unter Serialisierung versteht man in der Softwareentwicklung die Abbildung von Objekten auf eine externe Darstellungsform, um diese persistent zu machen. Im Allgemeinen könnte man die Eigenschaften der Objekte händisch in einem selbst definierten Format in Dateien schreiben. Die Serialisierung macht es dem Programmierer aber einfacher, indem dieser einfach komplette Objekte übergibt und das System übernimmt die strukturierte Ablage. C# bietet neben binärer Serialisierung Ihrer Objekte auch die Möglichkeit, Objekte in eine XML-Struktur zu serialisieren. Dies hat den Vorteil, dass die erzeugten Dateien in einem bestimmten Maße noch menschenlesbar sind. Dies wird allerdings dadurch erkauft, dass die Strukturierung durch XML im Verhältnis zur gepseicherten Menge vergleichsweise viel Speicherplatz verschwendet.

Vorbereitung

Betrachten wir eine einfache Klasse Mitarbeiter. Wie Sie sehen, speichert jedes Objekt dieser Klasse nur den Namen und den Vornamen einen Mitarbeiters, wobei diese Werte im Konstruktor gesetzt werden. Über die Properties ist der Zugriff auf die Eigenschaften nachträglich möglich.

public class Mitarbeiter
{
    private string _name;
    private string _vorname;

    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }

    public string Vorname
    {
        get
        {
            return _vorname;
        }
        set
        {
            _vorname = value;
        }
    }

    public Mitarbeiter(string name, string vorname)
    {
        this._name = name;
        this._vorname = vorname;
    }
}

Der öffentlich Zugriff ist zum Serialiseren wichtig. Nur öffentliche Eigenschaften oder Properties können serialisiert werden. Für einfache Datenklassen könnte man also auch auf die Properties verzichten und die Eigenschaft veröffentlichen. Bevor Objekte dieser Klasse aber in XML serialisiert werden können, müssen die öffentlichen Mitglieder entsprechent markiert werden, damit das System ‘weiß’, in welcher Form diese schließlich in der XML-Datei abgelegt werden. Als wichtige Deklarationen sind dabei “XmlAttribute” und “XmlElement” zu nennen. Nicht zu serialisierende öffentliche Mitglieder können mit “XmlIgnore” gekennzeichnet werden. Um diese nutzen zu können, muss der namespace “System.Xml.Serialization” eingebunden werden. Für die Deserialisierung, also das Wiedereinlesen der XML-Datei ist ein parameterloser Konstruktor Pflicht, da dieser dabei aufgerufen wird. Die Werte der Attribute werden anschließend über die Property-Setter bzw. über direkt in die öffentlichen Eigenschaften gesetzt.

public class Mitarbeiter
{
    //[...]

    [XmlAttribute]
    public string Name
    {
        //[...]
    }

    [XmlAttribute]
    public string Vorname
    {
        //[...]
    }

    //[...]

    ///
    /// Konstruktor für Deserialisierung
    ///
    public Mitarbeiter()
    {
    }
}

Serialisieren

Wenn Sie nun ein Objekt der Klasse Mitarbeiter serialisieren möchten, reichen diese wenigen Zeilen Quelltext bereits aus:

Mitarbeiter ma = new Mitarbeiter("Max","Mustermann");
XmlSerializer serializer = new XmlSerializer(typeof(Mitarbeiter));
FileStream fs = new FileStream("mitarbeiter.xml", FileMode.Create);
serializer.Serialize(fs, ma);
fs.Close();

Die resultierende XML-Datei sieht dann in etwa folgendermaßen aus (unwichtige Details ausgelassen). Da die Eigenschaften des Mitarbeiters als “XmlAttribute” serialisiert werden, erfolgt die Speicherung direkt im Mitarbeiter-Tag.

<?xml version="1.0"?>
<Mitarbeiter xmlns:xsi="..." xmlns:xsd="..." Name="Max" Vorname="Mustermann"/>

Verwendet man anstelle von “XmlAttribute” die Deklaration “XmlElement” erhält man dagegen folgendes Ergebnis:

<?xml version="1.0"?>
<Mitarbeiter xmlns:xsi="..." xmlns:xsd="...">
  <Name>Max</Name>
  <Vorname>Mustermann</Vorname>
</Mitarbeiter>

Nun ist das ausgewählt Beispiel sehr trivial gewählt und es wird in Realität eher nicht der Fall sein, dass solch ein einzelnes kleines Objekt serialisiert werden soll. Viel wahrscheinlicher ist die Speicherung von Listen von Objekten oder gar Aggregationen oder Kompositionen von verschiedenen Objekten. Wenn Sie die bisherigen Ausführen soweit verstanden haben, stellt das aber auch kein größeres Problem dar. Wir passen den Code zunächst auf die Speicherung von Listen von Mitarbeitern an:

List<Mitarbeiter> malist = new List<Mitarbeiter>();
malist.Add(new Mitarbeiter("Max","Mustermann"));
malist.Add(new Mitarbeiter("Martina","Musterfrau"));

XmlSerializer serializer = new XmlSerializer(typeof(List<Mitarbeiter>));
FileStream fs = new FileStream("mitarbeiter.xml", FileMode.Create);
serializer.Serialize(fs, malist);
fs.Close();

Wie man schön sieht, ändert sich der eigentliche Quelltext für das Serialisieren nur unwesentlich. Einzig der übergebene Typ und das konkrete zu serialisierende Objekt wurden verändert. Die resultende XML-Datei schaut dann ungefähr so aus:

<?xml version="1.0"?>
<ArrayOfMitarbeiter xmlns:xsi="..." xmlns:xsd="...">
  <Mitarbeiter Name="Max" Vorname="Mustermann"/>
  <Mitarbeiter Name="Martina" Vorname="Musterfrau"/>
</ArrayOfMitarbeiter>

Schauen wir uns nun an, wie sich Aggregationen serialisieren lassen. Dazu erstellen wir eine Klasse Abteilung. In einer Abteilung arbeiten für gewöhnlich mehrere Mitarbeiter. Das Listing ist diesmal bereits mit den nötigen XML-Deklarationen. Ich möchte an dieser Stelle darauf hinweisen, das die dargestellte Klasse kein gutes Beispiel für Objektorientierung ist. Für die Verdeutlichung der XML-Serialisierung sollte es aber ausreichend sein.

public class Abteilung
{
    [XmlAttribute("Abteilungsname")]
    public string Bezeichnung;

    [XmlArray("Angestellte")]
    [XmlArrayItem("Mitarbeiter", Type = typeof(Mitarbeiter))]
    public Mitarbeiter[] Mitarbeiter;

    public Abteilung(string bezeichnung)
    {
        this.Bezeichnung = bezeichnung;
    }

    public Abteilung()
    {
    }
}

Der Quelltext zum Serialisieren ist nun natürlich wieder entsprechend anzupassen. Es ist darauf zu achten, dass alle aggregierten Typen ebenfalls serialisiert werden können. In unserem Fall haben wir die Klasse Mitarbeiter bereits vorher behandelt und dies ist sichergestellt.

Existieren verschiedene Unterklassen der Klasse Mitarbeiter, so lassen sich die verschiedenen möglichen Objekte die die Klasse Abteilung führen kann folgendermaßen erweitern:

[XmlArray("Angestellte")]
[XmlArrayItem("Mitarbeiter", Type = typeof(Mitarbeiter)),
 XmlArrayItem("Abteilungsleiter", Type = typeof(Abteilungsleiter))]
public Mitarbeiter[] Mitarbeiter;

Ein mögliches Resultat für eine serialisierte Abteilung mit entsprechenden aggregierten Objekten könnte dann folgendermaßen aussehen. Man beachte, wie für die XML-Elemente und XML-Attribute die Zeichenketten verwendet wurde, die bei der Deklaration zusätzlich angegeben wurden.

<?xml version="1.0"?>
<Abteilung xmlns:xsi="..." xmlns:xsd="..." Abteilungsname="Buchhaltung">
  <Angestellte>
    <Abteilungsleiter Name="Klaus" Vorname="Freundlich"/>
    <Mitarbeiter Name="Max" Vorname="Mustermann"/>
    <Mitarbeiter Name="Martina" Vorname="Musterfrau"/>
  </Angestellte>
</Abteilung>

Deserialisieren

Nachdem die Serialisierung ziemlich umfangreich erörtert wurde, braucht man evtl. noch eine kleine Information, wenn man die entsprechenden XML-Daten später wieder einlesen möchte. Hierzu genügen, ähnlich wie es beim Serialisieren der Fall ist, nur wenige Zeilen Quelltext. Dieser kurze Ausschnitt passt zu dem ersten Quelltext der Serialisierung, bei dem nur die einfache Mitarbeiter-Klasse gespeichert wurde:

string filename = "mitarbeiter.xml";
if (File.Exists(filename))
{
    XmlSerializer serializer = new XmlSerializer(typeof(Mitarbeiter));
    FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
    Mitarbeiter ma = (Mitarbeiter)serializer.Deserialize(fs);
    fs.Close();
}

Wie man unschwer erkennen kann, könnte man für das Serialisieren und Deserialisieren dasselbe XmlSerializer-Objekt verwenden. Zum Deserialisieren der anderen beiden Beispiele müsste man ähnlich verfahren. Die Typen und der Cast sind dabei entsprechend anzupassen. Der Quelltext für das Serialisieren und Deserialisieren ist stets äquivalent zu betrachten.

Bookmark and Share