1. Einführung
XML (eXtensible Markup Language) — eine erweiterbare Markup-Sprache, ein verbreitetes Format zur Speicherung und zum Austausch komplex strukturierter Daten. Im Unterschied zu JSON ist XML weniger kompakt, wird aber noch immer breit eingesetzt, z. B. im Bankwesen, beim Datenaustausch zwischen staatlichen Diensten, in Dokumenten und Integrationen mit 1C, SAP, Oracle usw. Manche Daten in Windows und .NET (z. B. Configs) werden weiterhin im XML-Format gehalten.
Optisch ähnelt XML HTML, hat aber keinen festen Satz an Tags wie <body> oder <div> — du definierst selbst die Struktur deiner Elemente. Beispiel:
<Person>
<FirstName>Ivan</FirstName>
<LastName>Petrov</LastName>
<Age>30</Age>
</Person>
Grundlagen von XML und Zuordnung zu C#-Objekten
Im Gegensatz zu JSON kennt XML keine speziellen Typen für Arrays oder Booleans — alles wird über Elemente und Attribute dargestellt. Listenstrukturen werden durch wiederholte Elemente mit demselben Namen abgebildet.
Um in C# bequem mit XML zu arbeiten, verwendet man den Mechanismus der Serialisierung/Deserialisierung über den XmlSerializer.
Сериализация — die Umwandlung eines C#-Objekts in einen XML-String. Десериализация — der umgekehrte Prozess: Erzeugung eines C#-Objekts aus XML.
Das funktioniert "out of the box", wenn deine Klassen einige Anforderungen erfüllen — dazu gleich mehr.
Warum XmlSerializer verwenden?
XmlSerializer — das Standard-Tool in .NET (Namespace System.Xml.Serialization) zur Umwandlung von Objekten in XML und zurück. Es ist einfach, typ-sicher und eignet sich gut für Import/Export von Daten, Configs, System-zu-System-Austausch und Integrationen mit staatlichen Diensten.
2. Beispiel: Serialisierung eines Objekts
Angenommen, wir haben ein User-Modell:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Wir serialisieren ein Objekt in XML:
using System.Xml.Serialization;
User user = new User { FirstName = "Iwan", LastName = "Petrow", Age = 30 };
// Für die Serialisierung wird der Typ benötigt
XmlSerializer serializer = new XmlSerializer(typeof(User));
// Schreiben des XML in eine Datei
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);
Was passiert?
- Wir erstellen ein Objekt der Klasse User.
- Wir erstellen eine Instanz von XmlSerializer für den Typ User.
- Wir öffnen eine Datei mit FileStream.
- Wir rufen die Methode Serialize() auf, die das Objekt in XML umwandelt und in die Datei schreibt.
Inhalt der Datei user.xml:
<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>Iwan</FirstName>
<LastName>Petrow</LastName>
<Age>30</Age>
</User>
So einfach — minimaler Code und fertiges XML.
3. Deserialisierung: XML zurück in ein Objekt lesen
Wir lesen das erzeugte XML und stellen das C#-Objekt wieder her:
// Öffne die XML-Datei
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, Alter: {loadedUser?.Age}");
Die Methode Deserialize() gibt ein object zurück, daher casten wir auf User (mit as). Wenn die Datei korrekt ist und dem Modell entspricht, wird das Objekt vollständig wiederhergestellt.
4. Wie Serialisierung funktioniert: typische Besonderheiten
Für die XML-Serialisierung müssen Regeln beachtet werden:
• Die Klasse muss einen öffentlichen parameterlosen Konstruktor haben (public ohne Parameter).
• Serialisierbare Properties sind public: sie benötigen sowohl Getter als auch Setter. Private Member werden ignoriert.
• Read-only Properties, statische und abstrakte Properties funktionieren nicht.
Bei Verletzung der Regeln erhältst du eine InvalidOperationException mit einer Meldung wie "XmlSerializer cannot serialize this type".
5. Serialisierung von Collections: Arrays und Listen
Wenn mehrere Benutzer serialisiert werden sollen, verwendet man eine Wrapper-Klasse:
public class UserList
{
public List<User> Users { get; set; } = new List<User>();
}
Wir erstellen eine Collection und serialisieren sie:
UserList users = new UserList
{
Users = new List<User>
{
new User { FirstName = "Iwan", LastName = "Petrow", Age = 30 },
new User { FirstName = "Maria", LastName = "Ivanowa", Age = 25 }
}
};
XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);
Erzeugtes XML:
<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Users>
<User>
<FirstName>Iwan</FirstName>
<LastName>Petrow</LastName>
<Age>30</Age>
</User>
<User>
<FirstName>Maria</FirstName>
<LastName>Ivanowa</LastName>
<Age>25</Age>
</User>
</Users>
</UserList>
Kann man einfach List<User> serialisieren? Ja, aber das Wurzelelement heißt dann ArrayOfUser, was nicht immer für Integrationen passt.
6. Attribute zur Steuerung der Serialisierung
Mit Attributen kannst du das XML-Format steuern: Elementnamen ändern, als Attribute serialisieren, Felder ignorieren usw. Nützliche Attribute: [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore].
Als Attribute serialisieren
Speichere Vor- und Nachname als Attribute, das Alter als Element:
public class User
{
[XmlAttribute]
public string FirstName { get; set; }
[XmlAttribute]
public string LastName { get; set; }
[XmlElement]
public int Age { get; set; }
}
Ergebnis:
<User FirstName="Iwan" LastName="Petrow">
<Age>30</Age>
</User>
Steuerung der Elementnamen
Benennen wir die Tags menschenlesbar um:
public class User
{
[XmlElement("Vorname")]
public string FirstName { get; set; }
[XmlElement("Nachname")]
public string LastName { get; set; }
[XmlElement("Alter")]
public int Age { get; set; }
}
Resultierendes XML:
<User>
<Vorname>Iwan</Vorname>
<Nachname>Petrow</Nachname>
<Alter>30</Alter>
</User>
Eigenschaften ignorieren
Wenn eine Property nicht serialisiert werden soll — markiere sie mit [XmlIgnore]:
public class User
{
public string FirstName { get; set; }
[XmlIgnore]
public string InternalCode { get; set; }
}
7. Serialisierung von Hierarchien und verschachtelten Objekten
Verschachtelte Klassen werden einfach zu verschachtelten XML-Elementen:
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class User
{
public string FirstName { get; set; }
public Address Address { get; set; }
}
var user = new User
{
FirstName = "Anna",
Address = new Address { City = "Neongrad", Street = "Vyazovaya" }
};
XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);
XML:
<User>
<FirstName>Anna</FirstName>
<Address>
<City>Neongrad</City>
<Street>Vyazovaya</Street>
</Address>
</User>
8. Serialisierung in/aus einem String (ohne Dateien)
Serialisierung in einen String
using System.Text;
var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();
serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);
Deserialisierung aus einem String
var xml = "<User><FirstName>Anna</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);
9. Beispiel: Export und Import von Anwendungsdaten
Export/Import einer Benutzerliste in eine XML-Datei:
public static void ExportUsers(UserList users, string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Create);
serializer.Serialize(fs, users);
}
public static UserList ImportUsers(string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Open);
return (UserList)serializer.Deserialize(fs);
}
Anwendung: Backup, Austausch mit anderen Services, Integrationen. In Interviews kommen Fragen zu XmlSerializer öfter vor, als man denkt — besonders in Firmen, die mit staatlichen Diensten und Banken arbeiten.
10. Typische Fehler und Verhaltensweisen
Wenn ein Feld nicht public ist, wird es nicht serialisiert.
Read-only Properties werden ignoriert.
Properties mit einem Interface-Typ oder abstrakten Typen unterstützt der Serializer nicht.
Der Typ DateTime wird im XSD-Format serialisiert (siehe Dokumentation).
Collections wie Arrays und List<T> verhalten sich erwartbar; bei ObservableCollection<T> können bei Fremdsystemen Überraschungen auftreten.
Feinheiten bei Encodings: beim Schreiben mit StreamWriter ohne explizite Angabe der Kodierung kann das Ergebnis in UTF-16 landen; für Kompatibilität erwartet man häufiger UTF-8 — gib die Kodierung also explizit an.
GO TO FULL VERSION