1. Einführung
Ein Dictionary (oder Dictionary<TKey, TValue> in C#) ist eine Sammlung von "Schlüssel-Wert"-Paaren. Dieser Datentyp ist unersetzlich, wenn man schnell einen Wert nach einem eindeutigen Identifier finden muss (z. B. ein Telefon nach Namen im Telefonbuch suchen).
Im Gegensatz zu Listen (List<T>), bei denen die Reihenfolge der Elemente wichtig ist, konzentriert sich das Dictionary auf schnellen Zugriff per Schlüssel. Aber während die Serialisierung einer Liste einfach ist (JSON-Array), bringt die Serialisierung eines Dictionaries einige Nuancen mit sich:
- Der Schlüssel muss ein serialisierbarer Typ sein (meistens string, aber manchmal kann es auch eine Zahl oder sogar ein anderes Objekt sein).
- In JSON gibt es keine speziellen "Dictionaries" — nur Objekte oder Arrays.
Lass uns genauer betrachten, wie .NET Dictionaries serialisiert, welche Schwierigkeiten auftreten können und wie man den Code richtig "lehrt", mit solchen Strukturen zu arbeiten.
2. Serialisierung eines Dictionaries mit string-Schlüsseln
Fangen wir mit dem Klassiker an — ein Dictionary, bei dem sowohl Schlüssel als auch Wert strings sind.
// Beispiel-Dictionary: Buch und sein Autor
var books = new Dictionary<string, string>
{
["The Master and Margarita"] = "Michail Bulgakov",
["Harry Potter"] = "Joanne Rowling",
["Herr der Fliegen"] = "William Golding"
};
// Serialisierung in einen JSON-String
string json = JsonSerializer.Serialize(books, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine("Dictionary wurde in JSON serialisiert:\n" + json);
// In eine Datei schreiben (synchron)
File.WriteAllText("books.json", json);
Console.WriteLine("JSON in die Datei books.json geschrieben.");
// Wieder aus der Datei lesen (synchron)
string jsonFromFile = File.ReadAllText("books.json");
// Deserialisierung zurück ins Dictionary
var restoredBooks = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonFromFile);
Console.WriteLine("Ergebnis der Deserialisierung:");
foreach (var pair in restoredBooks)
Console.WriteLine($"{pair.Key} -> {pair.Value}");
Was landet in der Datei books.json?
{
"The Master and Margarita": "Michail Bulgakov",
"Harry Potter": "Joanne Rowling",
"Krieg und Frieden": "William Golding"
}
Wie funktioniert das?
Der Serializer verwandelt unser Dictionary<string, string> in ein JSON-Objekt, wobei jeder Schlüssel zum Property-Namen und der Wert zum Property-Wert wird. Das ist praktisch, wenn die Schlüssel strings und eindeutig sind.
3. Dictionary mit nicht-standardmäßigem Schlüsseltyp
Es ist einfach, solange der Schlüssel ein string ist. Was ist, wenn es z. B. eine Zahl ist?
var bookIds = new Dictionary<int, string>
{
[1001] = "The Master and Margarita",
[1002] = "Harry Potter",
[1003] = "Herr der Fliegen"
};
string jsonIntKeys = JsonSerializer.Serialize(bookIds, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonIntKeys);
Ergebnis:
{
"1001": "The Master and Margarita",
"1002": "Harry Potter",
"1003": "Herr der Fliegen"
}
Was ist passiert?
- C# hat die numerischen Schlüssel in strings umgewandelt, weil in JSON Property-Namen nur strings sein können.
- Bei der Deserialisierung zurück zu Dictionary<int, string> wird der Serializer versuchen, die Strings wieder in Zahlen zu konvertieren.
Beispiel der Deserialisierung:
var restoredBookIds = JsonSerializer.Deserialize<Dictionary<int, string>>(jsonIntKeys);
// Alles funktioniert! Die Schlüssel sind wieder Zahlen.
Was, wenn der Schlüssel ein komplexer Typ, z. B. ein Objekt, ist?
var dict = new Dictionary<Author, string>
{
[new Author { Name = "Golding", BirthYear = 1911 }] = "Herr der Fliegen"
};
Der Versuch, so ein Dictionary zu serialisieren, führt zu einer Exception:
System.NotSupportedException: Serialization and deserialization of 'Dictionary<Author, string>' instances are not supported.
Warum?
Ein JSON-Objekt kann nur strings als Property-Namen verwenden. Daher müssen die Schlüssel im Dictionary beim Serialisieren einfache Typen sein, die eindeutig in einen string übersetzbar sind (meist string oder Zahl). Komplexe Objekte können nicht als Schlüssel bei der Standard-JSON-Serialisierung verwendet werden.
4. Dictionary mit verschachtelten Objekten als Wert
Die Schlüssel haben wir geklärt — schauen wir jetzt, was passiert, wenn der Wert ein komplexes Objekt ist (z. B. Book oder Author).
Beispiel
public class Author
{
public string Name { get; set; }
public int BirthYear { get; set; }
}
var authorDirectory = new Dictionary<string, Author>
{
["bulgakov"] = new Author { Name = "Michail Bulgakov", BirthYear = 1891 },
["golding"] = new Author { Name = "William Golding", BirthYear = 1911 }
};
string jsonAuthors = JsonSerializer.Serialize(authorDirectory, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonAuthors);
Ergebnis:
{
"bulgakov": {
"Name": "Michail Bulgakov",
"BirthYear": 1891
},
"golding": {
"Name": "William Golding",
"BirthYear": 1911
}
}
- Alles, was verschachtelt ist, wird entsprechend der Objektstruktur serialisiert.
- Die Deserialisierung zurück zu Dictionary<string, Author> funktioniert ebenfalls problemlos.
5. Dictionary als Teil eines anderen Objekts
Sehr häufig werden Dictionaries als Feld innerhalb eines größeren Objekts verwendet. Beispiel: eine Bibliothek hat einen Katalog von Büchern, wobei jeder Schlüssel der Genre-Name ist und der Wert eine Liste von Büchern dieses Genres.
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class Library
{
public Dictionary<string, List<Book>> CatalogByGenre { get; set; }
}
var library = new Library
{
CatalogByGenre = new Dictionary<string, List<Book>>
{
["Fantasie"] = new List<Book>
{
new Book { Title = "Solaris", Author = "Stanislaw Lem" }
},
["Klassiker"] = new List<Book>
{
new Book { Title = "Herr der Fliegen", Author = "William Golding" },
new Book { Title = "Visible Darkness", Author = "William Golding" }
}
}
};
string jsonLibrary = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonLibrary);
Fragment des resultierenden JSON:
{
"CatalogByGenre": {
"Fantasie": [
{
"Title": "Solaris",
"Author": "Stanislaw Lem"
}
],
"Klassiker": [
{
"Title": "Herr der Fliegen",
"Author": "William Golding"
},
{
"Title": "Visible Darkness",
"Author": "William Golding"
}
]
}
}
Alles funktioniert — verschachtelte Dictionaries und Collections werden rekursiv serialisiert und deserialisiert.
6. Besonderheiten und Fallstricke der Dictionary-Serialisierung
Duplicate keys
In einem Dictionary sind Schlüssel immer eindeutig. Wenn man aber manuell JSON mit doppelten Schlüsseln einfügt:
{
"foo": "first",
"foo": "second"
}
Ergebnis: der letzte Wert ("second") überschreibt den ersten, es wird kein Fehler geworfen. So verhalten sich die meisten JSON-Parser.
Reihenfolge der Elemente
Dictionary ist eine ungeordnete Sammlung. Bei der Serialisierung kann die Reihenfolge der Schlüssel im JSON von der ursprünglichen abweichen. Wenn die Reihenfolge wichtig ist, verwendet eine Liste von Paaren (List<KeyValuePair<string, T>>), aber normalerweise ist die Reihenfolge in einem Dictionary egal.
JSON und verschachtelte Dictionaries
Die Verschachtelungstiefe ist nicht begrenzt, aber für korrektes Verhalten muss jede Ebene den JSON-Regeln entsprechen (Schlüssel — strings, Werte — gültige JSON-Objekte/Arrays).
Verwendung von JsonSerializerOptions
Manchmal sollen Property-Namen nicht in PascalCase, sondern in camelCase sein. Das ist wichtig, wenn man sich mit einem JavaScript-Frontend integriert, wo camelCase üblicher ist.
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
string camelJson = JsonSerializer.Serialize(authorDirectory, options);
Wichtig: Bei Dictionaries wirkt diese Option nur auf die Serialisierung verschachtelter Objekte (deren Properties), nicht auf die Dictionary-Schlüssel. Die Schlüssel werden so serialisiert, wie sie in C# als string angegeben wurden.
7. Probleme mit komplexen und nicht-string-Schlüsseln
Bei der Serialisierung von Dictionaries mit string- und numerischen Schlüsseln (z. B. int, long, Guid) funktioniert alles "out of the box". Wenn du jedoch versuchst, einen benutzerdefinierten class- oder struct-Typ als Schlüssel zu verwenden, bekommst du eine NotSupportedException.
Für solche Fälle gibt es Umgehungen:
- Ein anderes Speicherformat verwenden, z. B. das Dictionary als Array von Objekten mit Feldern "Key" und "Value" serialisieren.
- Einen Converter (JsonConverter) schreiben, der deinen komplexen Schlüssel in einen string und zurück konvertiert.
- Wenn die Struktur wirklich komplex ist — die Architektur überdenken und vermeiden, komplexe Objekte als Dictionary-Schlüssel zu verwenden.
Beispiel: Umgehung durch Serialisierung als Liste von Paaren
public class AuthorInfo
{
public Author Author { get; set; }
public string Book { get; set; }
}
// statt Dictionary<Author, string>
var list = new List<AuthorInfo>
{
new AuthorInfo { Author = new Author { Name = "William Golding", BirthYear = 1911 }, Book = "Herr der Fliegen" }
};
// so eine Liste lässt sich problemlos serialisieren
Vergleich: Dictionary vs. Liste von Paaren bei der Serialisierung
| Collection-Typ | JSON-Struktur | Wann verwenden |
|---|---|---|
|
|
Schlüssel sind einfache strings, braucht schnelle Suche und Eindeutigkeit |
|
|
Schlüssel sind komplex, Reihenfolge wichtig, Duplikate möglich |
8. Typische Interview-Fragen
1. Kann man Dictionary<DateTime, string> serialisieren?
Ja, aber die Schlüssel werden in eine string-Representation umgewandelt (häufig ISO-Format wie "yyyy-MM-ddTHH:mm:ss"). Bei der Deserialisierung kann es Probleme mit Locale- und Datumsformaten geben.
2. Was passiert, wenn man Dictionary<int, string> serialisiert?
Die Schlüssel werden als strings serialisiert, auch wenn sie im ursprünglichen Dictionary Zahlen waren. Beim Deserialisieren wird das zurückkonvertiert.
3. Warum kann man kein Dictionary mit Objekten als Schlüssel serialisieren?
Nur strings können Property-Namen in einem JSON-Objekt sein, Objekte können das nicht.
4. Was, wenn man ein Dictionary mit komplexen Schlüsseln serialisieren möchte?
Besser die Struktur überdenken oder als Liste von "key-value"-Paaren serialisieren, wobei der Schlüssel komplett als Objekt-Feld serialisiert wird, anstatt als Property-Name.
GO TO FULL VERSION