Serialisierung: Unterschied zwischen den Versionen

Aus Das Sopra Wiki
Zur Navigation springen Zur Suche springen
Keine Bearbeitungszusammenfassung
 
(28 dazwischenliegende Versionen von 9 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Serialisierung<ref name="RunTimeRerial">http://msdn.microsoft.com/en-us/magazine/cc301761.aspx Jeffrey Richter: Run-time Serialization</ref> bezeichnet den Vorgang, ein [[Objekt]] in einen Datenstrom umzuwandeln. Dieser kann dann auf einer Festplatte gespeichert oder über ein Netzwerk übertragen werden.
{{review}}
{{löschen}}
{{Navi:Implementierung}}
Serialisierung<ref name="RunTimeRerial">http://msdn.microsoft.com/en-us/magazine/cc301761.aspx Jeffrey Richter: Run-time Serialization</ref> bezeichnet den Vorgang, ein [https://docs.microsoft.com/de-de/dotnet/api/system.object?view=netcore-3.1 Objekt] in einen Datenstrom umzuwandeln. Dieser kann dann als auf einer Festplatte gespeichert oder über ein Netzwerk übertragen werden. C# stellt dafür Klassen bereit.


== Mehrere Möglichkeiten ==
== Formate ==
Es gibt mehrere Möglichkeiten, ein Objekt zu serialisieren.
Es gibt mehrere Möglichkeiten, ein Objekt zu serialisieren:
; Binary Serialization
; Binary Serialization
: Das Objekt wird in einem Binärformat gespeichert. Es kann nur von [[.NET]] gelesen werden.
: Das Objekt wird in einem Binärformat gespeichert. Es kann nur von [[.NET]] gelesen werden.
; XML Serialization
; XML Serialization
: Das Objekt wird in ein XML-Format serialisiert. Es kann dadruch relativ einfach auch auf anderen Plattformen gelesen werden.
: Das Objekt wird in ein XML-Format serialisiert. Es kann dadurch relativ einfach auch auf anderen Plattformen gelesen werden. Ausserdem kann man die Dateien als Mensch intuitiv verstehen und sogar editieren.
; SOAP
; SOAP
: Stadardisiertes Verfahren um Objekte zu serialisieren. Dadurch extrem einfach für andere Plattformen aber auch etwas schwerer für den Menschen zu lesen.
: Stadardisiertes Verfahren um Objekte zu serialisieren. Dadurch extrem einfach für andere Plattformen aber auch etwas schwerer für den Menschen zu lesen.


== Speichern ==
== Was kann Serialisiert werden ==
Um Ein Objekt zu serialisieren braucht es extrem wenig Code. Man sollte allerdings beachten, daß nur sinnvolle Daten serialisiert werden. Instanzen der Klasse <tt>File</tt> können aus offensichtlichen Gründen zum Beispiel nicht serialisiert werden.
Um Ein Objekt zu serialisieren braucht es extrem wenig Code. Man sollte allerdings beachten, daß nur sinnvolle Daten serialisiert werden. Instanzen der Klasse <tt>File</tt> können zum Beispiel nicht serialisiert werden. Der Grund ist, dass deren Inhalte nicht im Hauptspeicher liegen. Ebenfalls können einige XNA-Klassen nicht serialisiert werden, z.B. die <tt>Model</tt>-Klasse, weil Teile davon im Grafikkartenspeicher oder anderen Orten liegen. Wenn man nicht sicher ist, kann man sich mit F12 (oder Rechtsklick -> Go to Defninition) zum Code wechseln(oder zu den Metadaten). Und an den [[Serialisierung#Attribute|Attributen]] erkennen, ob sie serialisierbar sind und welche Felder ausgeschlossen werden.
Der XmlSerializer hat weitere Einschränkungen:
* Eine Klasse die Xml-serialisiert werden soll, braucht einen parameterlosen Konstrukor.
* keine Zweidimensionale Arrays
* (Die Klasse <tt>Dictionary</tt> und einige andere Klassen, von denen man es eigentlich nicht denkt, sind nicht erlaubt)


Als erstes markiert man die zu serialisierende Klasse mit dem Attribut
== Attribute ==
Klassen und Felder können [[Attribut|Attribute]] erhalten, ob sie serialisert werden sollen oder nicht. Wenn sie serialisiert werden sollen erhalten sie folgendes Attribut:
<source lang="csharp">
<source lang="csharp">
[Serializable]
[Serializable]
</source>
</source>
In der Standardeinstellung werden alle Felder und [[Property|Properties]] dieser Klasse serialisiert, auch die [[Sichtbarkeit|privaten]]. Um das zu verhindern kann man diese mit dem Attribut
In der Standardeinstellung werden alle Felder und [[Property|Properties]] dieser Klasse serialisiert, auch die [[Sichtbarkeit|privaten]]. Um das zu verhindern kann man diese mit folgendem Attribut markieren:
<source lang="csharp">
<source lang="csharp">
[NonSerialized]
[NonSerialized]
</source>
</source>
markieren.
Man muss allerdings beachten, daß <tt>XmlSerializer</tt> keine privaten Felder und keine private Properties serialisiert, sondern nur, was als public markiert ist.


Man muss allerdings beachten, daß <tt>XmlSerializer</tt> keine privaten Felder und Properties serialisiert.
Ein kleines Beispiel:
<source lang="csharp">
[Serializable]
public class GameUnit
{
  public int hitPoints;
  public Vector3 position;
  private UnitAi unitAi;
  [NonSerialized]
  public Model unitModel;
}
[Serializable]
public class UnitAi
{
  public StateMachineState state;
  private int abc;
}
</source>
Wenn eine Instanz dieser Klasse serialisiert und gespeichert wird, werden folgende Daten gespeichert:
* Beim BinaryFormatter
** Der Integer <tt>hitPoints</tt>
** Das Strukt <tt>position</tt> mit seinen Feldern
** Die Referenz <tt>unitAi</tt> und die Instanz auf die sie zeigt. Und entsprechend dann auch deren Felder <tt>state</tt> und <tt>abc</tt>.
* Beim XmlSerializer
** Der Integer <tt>hitPoints</tt>
** Das Strukt <tt>position</tt> mit seinen Feldern
** Die Referenz <tt>unitAi</tt> wird nicht serialisiert, da sie als private deklariert ist [[Kategorie:Code-Beispiele]]
 
== Referenzen ==
Referenzen werden in XMlSerializer und BinaryFormatter ein bisschen unterschiedlich behandelt. Da es in Xml keine Referenzen gibt, werden an derer Stelle einfach die Objekte eingeschachtelt. Während der BinaryFormatter dafür sorgt, dass nach dem Deserialisieren wieder alles so ist, wie vor dem Serialisieren. Es kann vorkommen, dass es im ganzen Serialisierungsvorgang eine Instanz mehrfach referenziert wird. Diese Instanz wird vom BinaryFormatter nur einmal serialisiert und beim Deserialisieren werden alle Referenzen wieder hergestellt. Auf diese Weise kann man eine komplette Objekthierarchie speichern. Nach dem Xml-Serialisierung und -Deserialisieren hat man jedoch für jede Referenz eine eigene Instanz, damit muss man vorsichtig sein.


== Laden ==
== BinaryFormatter ==  
Um ein Objekt dann in eine Datei zu speichern führt man folgenden Code aus:
Man binde folgendes ein:
<source lang="csharp">
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
</source>
Zum Speichern einer Instanz wird folgender Code verwendet:
<source lang="csharp">
<source lang="csharp">
System.IO.FileStream fs = System.IO.File.Open("DateiName", System.IO.FileMode.Truncate);
void SaveUnit(GameUnit gameUnit, String path)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
{
bf.Serialize(fs, DateTime.Now);
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, System.IO.FileMode.Create);
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(fs, gameUnit);
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
  }
  finally
  {
    // Immer den Stream schliessen
    if (fs != null)
    {
      fs.Close();
    }
  }
}
</source>
 
Zum Laden einer Instanz wird folgender Code verwendet:
<source lang="csharp">
GameUnit LoadUnit(String path)
{
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, FileMode.Open);
    BinaryFormatter bf = new BinaryFormatter();
                 
    return (GameUnit)bf.Deserialize(fs);
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
    return null;
  }
  finally
  {
    if (fs != null)
    {
      fs.Close();
    }
  }
}
</source>
 
== XmlSerializer ==
Man binde folgendes ein:
<source lang="csharp">
using System.IO;
using System.Xml.Serialization;
</source>
Um ein Objekt dann in eine Xml-Datei zu speichern führt man folgenden Code aus:
<source lang="csharp">
void SaveUnit(GameUnit gameUnit, String path)
{
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, System.IO.FileMode.Create);
    XmlSerializer seri = new XmlSerializer(typeof(GameUnit));
    seri.Serialize(fs, gameUnit);
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
  }
  finally
  {
    // Immer den Stream schliessen
    if (fs != null)
    {
      fs.Close();
    }
  }
}
</source>
</source>
Um das Objekt wieder einzulesen benutzt man folgende Zeilen:
Um das Objekt wieder einzulesen benutzt man folgende Zeilen:
<source lang="csharp">
<source lang="csharp">
System.IO.FileStream fs = System.IO.File.Open("DateiName", System.IO.FileMode.Open);
GameUnit LoadUnit(String path)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
{
DateTime dt = (DateTime)bf.Deserialize(fs);
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, FileMode.Open);
    XmlSerializer seri = new XmlSerializer(typeof(GameUnit));              
    return (GameUnit)seri.Deserialize(fs);  
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
    return null;
  }
  finally
  {
    if (fs != null)
    {
      fs.Close();
    }
  }
}
</source>
</source>
Man muss dabei beachten, daß die Klasse <tt>BinaryFormatter</tt><ref name="BinFormatter">http://msdn.microsoft.com/de-de/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx MSDN-Artikel zur Klasse BinaryFormatter</ref> wesentlich mehr kann als die Klasse <tt>XmlSerializer</tt><ref name=XmlSrielizer">http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx MSDN-Artikel zur Klasse XmlSerializer</ref>, die dafür auch auf der [[XBox 360]] zur Verfügung steht.
 
 
Man muss dabei beachten, daß die Klasse <tt>BinaryFormatter</tt><ref name="BinFormatter">http://msdn.microsoft.com/de-de/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx MSDN-Artikel zur Klasse BinaryFormatter</ref> wesentlich mehr kann als die Klasse <tt>XmlSerializer</tt><ref name=XmlSrielizer">http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx MSDN-Artikel zur Klasse XmlSerializer</ref>, die dafür auch auf der [[XBox 360]] zur Verfügung steht. [[Kategorie:Code-Beispiele]]


== Netzwerk ==
== Netzwerk ==
Man kann mit Serialisierung auch Daten über ein Netzwerk schicken. Das Pronzip ist das gleiche, da die Formatter auf Streams arbeiten. Was für einen Stream man nimmt ist ihnen egal. Für Netzwerkfunktionalität bieten sich entweder die speziellen [[XNA]]-Klassen dafür an, oder man schreibt die Funktionalität selbst. Dies ist flexibler, man braucht keinen [[XBox 360|XBox-Live]]-Account. Dafür funktioniert sie auf der XBox nicht.
{{BA|Greitschus|Das böse böse Netzwerk. Sollten wir das hier in so einem "guten Licht" dastehen lassen, wenn man's selbst schreibt, oder sollte man den Satz evtl vorsichtiger formulieren?}}
Man kann mit Serialisierung auch Daten über ein Netzwerk schicken. Das Prinzip ist das gleiche, da die Formatter auf Streams arbeiten. Was für einen Stream man nimmt ist ihnen egal. Für Netzwerkfunktionalität bieten sich entweder die speziellen [[XNA]]-Klassen dafür an, oder man schreibt die Funktionalität selbst. Dies ist flexibler und man braucht keinen [[XBox 360|XBox-Live]]-Account. Dafür funktioniert sie auf der XBox nicht.


Die nötigen Klassen finden sich im [[Namespace]]
Die nötigen Klassen finden sich im [[Namensraum]]
  System.Net.Sockets
  System.Net.Sockets


Zeile 71: Zeile 213:
</source> [[Kategorie:Code-Beispiele]]
</source> [[Kategorie:Code-Beispiele]]


== Video Tutorial ==
== XML ==
Auf MSDN gibt es auch einen Webcast zum Thema<ref name="SerWebcast">http://msevents.microsoft.com/cui/WebCastEventDetails.aspx?EventID=1032292924&EventCategory=3&culture=en-US&CountryCode=US MSDN Webcast: Advanced Serialization (Level 300)</ref><noinclude>
Für XML kann man sich folgende Klassen anschauen
* IntermediateSerializer [http://blogs.msdn.com/shawnhar/archive/2008/08/11/how-serializers-work.aspx] und [http://blogs.msdn.com/shawnhar/archive/2008/07/28/intermediateserializer-vs-xmlserializer.aspx]
* XmlSerializer [http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx]
* Leistungsfähiger als die beiden oben ist der SoapFormatter
 
{{BA|Vogty|hier befand sich noch ein Abschnitt "Video Tutorial" bestehend aus einer Verlinkung auf ein MSDN Video, dieses existiert aber nicht mehr, ich habe den Abschnitt entfernt}}


== Referenzen ==
== Referenzen ==
<references />
<references />
[[Kategorie:CSharp]][[Kategorie:Code-Beispiele]][[Kategorie:Begriffe]][[Kategorie:VideoTutorials]][[Kategorie:Tutorials]]</noinclude>
[[Kategorie:CSharp]][[Kategorie:Code-Beispiele]][[Kategorie:Begriffe]][[Kategorie:VideoTutorials]][[Kategorie:Tutorials]]</noinclude>
[[Kategorie:MS02]]
[[Kategorie:MS03]]
[[Kategorie:MS04]]

Aktuelle Version vom 21. Oktober 2020, 13:28 Uhr

Vorlage:Navi:Implementierung Serialisierung[1] bezeichnet den Vorgang, ein Objekt in einen Datenstrom umzuwandeln. Dieser kann dann als auf einer Festplatte gespeichert oder über ein Netzwerk übertragen werden. C# stellt dafür Klassen bereit.

Formate

Es gibt mehrere Möglichkeiten, ein Objekt zu serialisieren:

Binary Serialization
Das Objekt wird in einem Binärformat gespeichert. Es kann nur von .NET gelesen werden.
XML Serialization
Das Objekt wird in ein XML-Format serialisiert. Es kann dadurch relativ einfach auch auf anderen Plattformen gelesen werden. Ausserdem kann man die Dateien als Mensch intuitiv verstehen und sogar editieren.
SOAP
Stadardisiertes Verfahren um Objekte zu serialisieren. Dadurch extrem einfach für andere Plattformen aber auch etwas schwerer für den Menschen zu lesen.

Was kann Serialisiert werden

Um Ein Objekt zu serialisieren braucht es extrem wenig Code. Man sollte allerdings beachten, daß nur sinnvolle Daten serialisiert werden. Instanzen der Klasse File können zum Beispiel nicht serialisiert werden. Der Grund ist, dass deren Inhalte nicht im Hauptspeicher liegen. Ebenfalls können einige XNA-Klassen nicht serialisiert werden, z.B. die Model-Klasse, weil Teile davon im Grafikkartenspeicher oder anderen Orten liegen. Wenn man nicht sicher ist, kann man sich mit F12 (oder Rechtsklick -> Go to Defninition) zum Code wechseln(oder zu den Metadaten). Und an den Attributen erkennen, ob sie serialisierbar sind und welche Felder ausgeschlossen werden. Der XmlSerializer hat weitere Einschränkungen:

  • Eine Klasse die Xml-serialisiert werden soll, braucht einen parameterlosen Konstrukor.
  • keine Zweidimensionale Arrays
  • (Die Klasse Dictionary und einige andere Klassen, von denen man es eigentlich nicht denkt, sind nicht erlaubt)

Attribute

Klassen und Felder können Attribute erhalten, ob sie serialisert werden sollen oder nicht. Wenn sie serialisiert werden sollen erhalten sie folgendes Attribut:

[Serializable]

In der Standardeinstellung werden alle Felder und Properties dieser Klasse serialisiert, auch die privaten. Um das zu verhindern kann man diese mit folgendem Attribut markieren:

[NonSerialized]

Man muss allerdings beachten, daß XmlSerializer keine privaten Felder und keine private Properties serialisiert, sondern nur, was als public markiert ist.

Ein kleines Beispiel:

[Serializable]
public class GameUnit
{
  public int hitPoints;
  public Vector3 position;
  private UnitAi unitAi;
  [NonSerialized]
  public Model unitModel;
}
[Serializable]
public class UnitAi
{
  public StateMachineState state;
  private int abc;
}

Wenn eine Instanz dieser Klasse serialisiert und gespeichert wird, werden folgende Daten gespeichert:

  • Beim BinaryFormatter
    • Der Integer hitPoints
    • Das Strukt position mit seinen Feldern
    • Die Referenz unitAi und die Instanz auf die sie zeigt. Und entsprechend dann auch deren Felder state und abc.
  • Beim XmlSerializer
    • Der Integer hitPoints
    • Das Strukt position mit seinen Feldern
    • Die Referenz unitAi wird nicht serialisiert, da sie als private deklariert ist

Referenzen

Referenzen werden in XMlSerializer und BinaryFormatter ein bisschen unterschiedlich behandelt. Da es in Xml keine Referenzen gibt, werden an derer Stelle einfach die Objekte eingeschachtelt. Während der BinaryFormatter dafür sorgt, dass nach dem Deserialisieren wieder alles so ist, wie vor dem Serialisieren. Es kann vorkommen, dass es im ganzen Serialisierungsvorgang eine Instanz mehrfach referenziert wird. Diese Instanz wird vom BinaryFormatter nur einmal serialisiert und beim Deserialisieren werden alle Referenzen wieder hergestellt. Auf diese Weise kann man eine komplette Objekthierarchie speichern. Nach dem Xml-Serialisierung und -Deserialisieren hat man jedoch für jede Referenz eine eigene Instanz, damit muss man vorsichtig sein.

BinaryFormatter

Man binde folgendes ein:

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

Zum Speichern einer Instanz wird folgender Code verwendet:

void SaveUnit(GameUnit gameUnit, String path)
{
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, System.IO.FileMode.Create);
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(fs, gameUnit);
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
  }
  finally
  {
    // Immer den Stream schliessen
    if (fs != null)
    {
      fs.Close();
    }
  }
}

Zum Laden einer Instanz wird folgender Code verwendet:

GameUnit LoadUnit(String path)
{
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, FileMode.Open);
    BinaryFormatter bf = new BinaryFormatter();
                  
    return (GameUnit)bf.Deserialize(fs); 
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
    return null;
  }
  finally
  {
    if (fs != null)
    {
      fs.Close();
    }
  }
}

XmlSerializer

Man binde folgendes ein:

using System.IO;
using System.Xml.Serialization;

Um ein Objekt dann in eine Xml-Datei zu speichern führt man folgenden Code aus:

void SaveUnit(GameUnit gameUnit, String path)
{
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, System.IO.FileMode.Create);
    XmlSerializer seri = new XmlSerializer(typeof(GameUnit));
    seri.Serialize(fs, gameUnit);
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
  }
  finally
  {
    // Immer den Stream schliessen
    if (fs != null)
    {
      fs.Close();
    }
  }
}

Um das Objekt wieder einzulesen benutzt man folgende Zeilen:

GameUnit LoadUnit(String path)
{
  FileStream fs = null;
  try
  {
    fs = new FileStream(path, FileMode.Open);
    XmlSerializer seri = new XmlSerializer(typeof(GameUnit));                
    return (GameUnit)seri.Deserialize(fs); 
  }
  catch (Exception e)
  {
    // TODO: Fehlerbehandlung hier
    return null;
  }
  finally
  {
    if (fs != null)
    {
      fs.Close();
    }
  }
}


Man muss dabei beachten, daß die Klasse BinaryFormatter[2] wesentlich mehr kann als die Klasse XmlSerializer[3], die dafür auch auf der XBox 360 zur Verfügung steht.

Netzwerk

Man kann mit Serialisierung auch Daten über ein Netzwerk schicken. Das Prinzip ist das gleiche, da die Formatter auf Streams arbeiten. Was für einen Stream man nimmt ist ihnen egal. Für Netzwerkfunktionalität bieten sich entweder die speziellen XNA-Klassen dafür an, oder man schreibt die Funktionalität selbst. Dies ist flexibler und man braucht keinen XBox-Live-Account. Dafür funktioniert sie auf der XBox nicht.

Die nötigen Klassen finden sich im Namensraum

System.Net.Sockets

Hier ein kleines Beispiel für einen Server, der einen String entgegen nimmt.

TcpListener listener = new TcpListener(11111);
listener.Start();
using (var client = listener.AcceptTcpClient())
{
	Stream s = client.GetStream();
	BinaryFormatter bf = new BinaryFormatter();
	String str = (String)bf.Deserialize(s);
	Console.WriteLine(str);
	Console.ReadLine();
}
listener.Stop();

und hier der passende Client

using (TcpClient client = new TcpClient("localhost", 11111))
{
	Stream s = client.GetStream();
	BinaryFormatter bf = new BinaryFormatter();
	bf.Serialize(s, Console.ReadLine());
}
Console.ReadLine();

XML

Für XML kann man sich folgende Klassen anschauen

  • IntermediateSerializer [1] und [2]
  • XmlSerializer [3]
  • Leistungsfähiger als die beiden oben ist der SoapFormatter

Referenzen