1. Einführung
Du weißt bereits, dass .NET Objekte standardmäßig basierend auf öffentlichen Feldern und Properties in XML serialisieren und deserialisieren kann. Aber wenn du hübsches XML willst, das mit fremden Programmen/Standards kompatibel ist, oder wenn bestimmte interne Details nicht ins XML sollen, brauchst du feinere Kontrolle.
In C# gibt es dafür Serialisierungsattribute — spezielle "Markierungen", die wir an Klassen, Properties oder Feldern anbringen. Mit diesen Attributen kannst du buchstäblich alles steuern: welche Felder ins XML kommen, welche ignoriert werden, wie ein Element heißt, ob es als Attribut erscheint, ob es eine komplexe Struktur hat und sogar in welcher Reihenfolge Elemente stehen!
Stell dir vor, die Standard-Serialisierung ist wie das automatische Ausfüllen eines Formulars: Vorname-Nachname-Mittelname in ihre Felder, alles andere mehr oder weniger willkürlich. Mit Attributen bist du der Super-Office-Beamte: du entscheidest, welche Zeile wohin kommt, was übersprungen wird, was rot markiert wird und was ganz im geheimen Abschnitt verschwindet.
Abbildung Objekt zu XML
| Klasse/Property | Attribut | Ergebnis im XML |
|---|---|---|
|
Klasse UserList | |
|
|
|
|
|
id="..." (Attribut) |
|
|
|
|
|
|
|
[XmlIgnore] (siehe RegisteredAtString) | - |
|
|
|
|
|
|
2. Überblick über die wichtigsten XML-Serialisierungsattribute
Gehen wir die populärsten Attribute durch, mit denen .NET-Entwickler ihre Klassen seit vielen Generationen aufgehübscht haben!
Attribut [XmlElement] — Elementname
Ändert den Namen des Elements im XML oder markiert Property/Feld als XML-Element.
public class Person
{
[XmlElement("FullName")]
public string Name { get; set; }
}
XML:
<Person>
<FullName>Wassilij Petrov</FullName>
</Person>
Wenn du das Attribut nicht angibst, ist der Standard-Elementname derselbe wie der Property-/Feldname.
Attribut [XmlAttribute] — Serialisierung als XML-Attribut
Ermöglicht, eine Property/ein Feld als XML-Attribut statt als Element zu serialisieren.
public class Person
{
[XmlAttribute("id")]
public int Id { get; set; }
}
XML:
<Person id="123"></Person>
In realen XML-APIs verwendet man oft Attribute für IDs, Daten, Flags etc.
Attribut [XmlIgnore] — Feld/Property auslassen
Der Traum jedes Paranoikers: eine Property komplett aus Serialisierung und Deserialisierung ausschließen! Alles, was mit diesem Attribut markiert ist, gelangt nicht ins finale XML.
public class Person
{
[XmlIgnore]
public string InternalNote { get; set; }
}
XML: (Property InternalNote taucht hier nicht auf)
Attribute [XmlArray] und [XmlArrayItem] — Kontrolle über Collections
Spezielle Attribute für Arrays und Collections. [XmlArray] legt den Namen des äußeren Wrapper-Arrays fest, [XmlArrayItem] den Namen des einzelnen Elements.
public class Person
{
[XmlArray("Phones")]
[XmlArrayItem("Phone")]
public string[] PhoneNumbers { get; set; }
}
XML:
<Person>
<Phones>
<Phone>+12951234567</Phone>
<Phone>+12876543210</Phone>
</Phones>
</Person>
Attribut [XmlRoot] — Name des Root-Elements
Markiert die Klasse, um den Namen des Root-Elements im XML zu ändern.
[XmlRoot("User")]
public class Person
{
public string Name { get; set; }
}
XML:
<User>
<Name>Anna</Name>
</User>
Attribut [XmlText] — Serialisierung als Textinhalt eines Elements
Manchmal möchte man (oder die Spezifikation verlangt), dass der Wert einer Property einfach der Text innerhalb eines Elements ist, und nicht ein geschachteltes Sub-Element oder Attribut.
public class Note
{
[XmlText]
public string Content { get; set; }
}
XML:
<Note>Ruf die Oma an!</Note>
Attribute [XmlNamespaceDeclarations], [XmlElement(Type = ...)]
Für fortgeschrittene Szenarien (z. B. Serialisierung mit Namespaces, Unterstützung von Vererbung etc.) gibt es noch spezifischere Attribute. Schau dir unbedingt die offizielle Dokumentation zur XML-Serialisierung an für die vollständige Liste.
3. Praxis: Wir verbessern die Serialisierung an einem realen Beispiel
Schritt für Schritt bringen wir unsere Übungsanwendung auf "XML-Serialisierung Premium", damit sie sogar den strengsten XML-Inspektor erfreut.
Basis-Klasse ohne Attribute
Angenommen, wir haben diese User-Klasse:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string[] Emails { get; set; }
public DateTime RegisteredAt { get; set; }
public string Password { get; set; }
}
Die Default-Serialisierung liefert ungefähr dieses XML (die Datumsausgabe folgt ihrem eigenen Stil):
<User>
<Id>42</Id>
<Name>Ivan Ivanov</Name>
<Emails>
<string>user@mail.com</string>
<string>admin@site.org</string>
</Emails>
<RegisteredAt>2024-07-01T00:00:00</RegisteredAt>
<Password>qwerty</Password>
</User>
Was stört:
- Das Password sollte besser gar nicht serialisiert werden.
- Wir wollen das Datum als Attribut und in menschenlesbarem Format.
- Wir wollen Emails in einer hübschen Form.
- Wir wollen das Root-Element <Person>.
Fügen wir Attribute hinzu
[XmlRoot("Person")]
public class User
{
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement("FullName")]
public string Name { get; set; }
[XmlArray("EMails")]
[XmlArrayItem("Email")]
public string[] Emails { get; set; }
[XmlAttribute("registered")]
public string RegisteredAtString
{
get => RegisteredAt.ToString("yyyy-MM-dd");
set => RegisteredAt = DateTime.Parse(value);
}
[XmlIgnore]
public string Password { get; set; }
[XmlIgnore]
public DateTime RegisteredAt { get; set; }
}
Erklärung der Details:
- Wir verwenden [XmlAttribute("registered")], um das Datum als Attribut zu serialisieren.
- Um nicht das DateTime selbst zu serialisieren, sondern seine String-Repräsentation, fügen wir eine separate Property RegisteredAtString hinzu und verstecken das echte Feld über [XmlIgnore].
- Das Password verstecken wir mit [XmlIgnore].
- Das Email-Array ist so konfiguriert, dass das äußere Tag <EMails> heißt und jedes Email-Element <Email>.
Ergebnis:
<Person id="42" registered="2024-07-01">
<FullName>Ivan Ivanov</FullName>
<EMails>
<Email>user@mail.com</Email>
<Email>admin@site.org</Email>
</EMails>
</Person>
4. Arbeiten mit verschachtelten Objekten
XML spielt seine Stärken besonders aus, wenn es verschachtelte Objekte gibt (z. B. ein User und seine Adressen).
public class Address
{
[XmlAttribute]
public string City { get; set; }
[XmlText]
public string Details { get; set; }
}
public class User
{
// ...andere Properties, siehe oben...
public Address[] Addresses { get; set; }
}
Und wir fügen die Konfiguration für das Adress-Array hinzu:
[XmlArray("UserAddresses")]
[XmlArrayItem("Address")]
public Address[] Addresses { get; set; }
Dann sieht das XML so aus:
<Person id="42" registered="2024-07-01">
<FullName>Ivan Ivanov</FullName>
<EMails>
<Email>user@mail.com</Email>
</EMails>
<UserAddresses>
<Address City="Neon city">ul. Zamkovaja, d.1</Address>
<Address City="North Cave">ul. Krajnaja d.12</Address>
</UserAddresses>
</Person>
Man sieht, dass bei jedem <Address> city ein Attribut ist und die eigentliche Adresse als Text innerhalb steht.
5. Häufige Fehler und Feinheiten
Wenn du ein komplexes Objekt serialisierst und die erwarteten Änderungen im XML nicht siehst — meist hast du vergessen, das richtige Attribut zu setzen. Manchmal bleiben Properties/Felder unmarkiert und .NET wählt dann das Standardverhalten.
Eine andere Falle ist der Name einer Collection. Wenn eine Collection (z. B. ein String-Array) weder mit [XmlArray] noch mit [XmlArrayItem] markiert ist, werden die Elemente mit dem Tag string geschrieben (wie im Anfangsbeispiel). Damit das nicht passiert, vergib immer explizit Tag-Namen per Attributen.
Sei vorsichtig mit "getter-only" Properties — solche Properties werden standardmäßig nicht deserialisiert. Versuch, ein öffentliches set bereitzustellen.
Einige Typen (z. B. Dictionaries oder Interfaces) werden vom Standard-XmlSerializer nicht direkt serialisiert oder erfordern spezielle Behandlung.
GO TO FULL VERSION