Serialisierung: Unterschied zwischen den Versionen
Justus (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
LeonH (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
(32 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 [ | {{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. | |||
Es gibt mehrere Möglichkeiten, ein Objekt zu serialisieren | == Formate == | ||
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 | : 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. | ||
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 | == 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 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) | |||
== 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 | 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> | ||
Man muss allerdings beachten, daß <tt>XmlSerializer</tt> keine privaten Felder und keine private Properties serialisiert, sondern nur, was als public markiert ist. | |||
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. | |||
== BinaryFormatter == | |||
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"> | |||
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(); | |||
} | |||
} | |||
} | |||
</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> | |||
Um ein Objekt dann in eine Datei zu speichern führt man folgenden Code aus: | == 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"> | <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"> | ||
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(); | |||
} | |||
} | |||
} | |||
</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. [[Kategorie:Code-Beispiele]] | |||
== Netzwerk == | |||
{{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 [[Namensraum]] | |||
System.Net.Sockets | |||
Hier ein kleines Beispiel für einen Server, der einen String entgegen nimmt. | |||
<source lang="csharp"> | |||
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(); | |||
</source> | |||
und hier der passende Client | |||
<source lang="csharp"> | |||
using (TcpClient client = new TcpClient("localhost", 11111)) | |||
{ | |||
Stream s = client.GetStream(); | |||
BinaryFormatter bf = new BinaryFormatter(); | |||
bf.Serialize(s, Console.ReadLine()); | |||
} | |||
Console.ReadLine(); | |||
</source> [[Kategorie:Code-Beispiele]] | |||
== XML == | |||
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
- ↑ http://msdn.microsoft.com/en-us/magazine/cc301761.aspx Jeffrey Richter: Run-time Serialization
- ↑ http://msdn.microsoft.com/de-de/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx MSDN-Artikel zur Klasse BinaryFormatter
- ↑ http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx MSDN-Artikel zur Klasse XmlSerializer