1. Introduction
A dictionary (or Dictionary<TKey, TValue> in C#) is a collection of key-value pairs. This data type is indispensable when you need to quickly find a value by a unique identifier (for example, looking up a phone number by name in a phone book).
Unlike lists (List<T>), where element order matters, a dictionary focuses on fast access to data by key. But while serializing a list is straightforward (a JSON array), serializing a dictionary has some nuances:
- The key must be a serializable type (most often a string, but sometimes it can be a number or even another object).
- JSON doesn't have separate "dictionary" types — only objects or arrays.
Let's look in more detail at how .NET serializes dictionaries, what difficulties you might encounter, and how to properly "teach" your code to work with such structures.
2. Serializing a dictionary with string keys
Let's start with the classic — a dictionary where both the key and the value are strings.
// Example dictionary: a book and its author
var books = new Dictionary<string, string>
{
["Master and Margarita"] = "Mikhail Bulgakov",
["Harry Potter"] = "Joan Rowling",
["Lord of the Flies"] = "William Golding"
};
// Serialize to a JSON string
string json = JsonSerializer.Serialize(books, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine("Dictionary serialized to JSON:\n" + json);
// Save to a file (synchronously)
File.WriteAllText("books.json", json);
Console.WriteLine("JSON written to file books.json.");
// Read back from file (synchronously)
string jsonFromFile = File.ReadAllText("books.json");
// Deserialize back into a dictionary
var restoredBooks = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonFromFile);
Console.WriteLine("Deserialization result:");
foreach (var pair in restoredBooks)
Console.WriteLine($"{pair.Key} -> {pair.Value}");
What will appear in the books.json file?
{
"Master and Margarita": "Mikhail Bulgakov",
"Harry Potter": "Joan Rowling",
"War and Peace": "William Golding"
}
How does this work?
The serializer turns our Dictionary<string, string> into a JSON object where each key becomes a property name and the value becomes the property value. This is convenient if keys are strings and they are unique.
3. Dictionary with a nonstandard key type
It's simple while the key is a string. What if it's, say, a number?
var bookIds = new Dictionary<int, string>
{
[1001] = "Master and Margarita",
[1002] = "Harry Potter",
[1003] = "Lord of the Flies"
};
string jsonIntKeys = JsonSerializer.Serialize(bookIds, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonIntKeys);
Result:
{
"1001": "Master and Margarita",
"1002": "Harry Potter",
"1003": "Lord of the Flies"
}
What happened?
- C# converted numeric keys to strings because JSON property names can only be strings.
- When deserializing back into Dictionary<int, string>, the serializer will try to convert the string back to a number.
Deserialization example:
var restoredBookIds = JsonSerializer.Deserialize<Dictionary<int, string>>(jsonIntKeys);
// Everything works! Keys are numbers again.
What if the key is a complex type, e.g., an object?
var dict = new Dictionary<Author, string>
{
[new Author { Name = "Golding", BirthYear = 1911 }] = "Lord of the Flies"
};
Attempting to serialize such a dictionary will throw an exception:
System.NotSupportedException: Serialization and deserialization of 'Dictionary<Author, string>' instances are not supported.
Why is that?
A JSON object can't use anything other than a string as a property name. So dictionary keys during serialization must be simple types that can be unambiguously converted to a string (usually string or number). Complex objects can't be used as keys when serializing to JSON using the default tools.
4. Dictionary with nested objects as values
Keys are clear — now let's see what happens if the value is a complex object (for example, Book or Author).
Example
public class Author
{
public string Name { get; set; }
public int BirthYear { get; set; }
}
var authorDirectory = new Dictionary<string, Author>
{
["bulgakov"] = new Author { Name = "Mikhail Bulgakov", BirthYear = 1891 },
["golding"] = new Author { Name = "William Golding", BirthYear = 1911 }
};
string jsonAuthors = JsonSerializer.Serialize(authorDirectory, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonAuthors);
Result:
{
"bulgakov": {
"Name": "Mikhail Bulgakov",
"BirthYear": 1891
},
"golding": {
"Name": "William Golding",
"BirthYear": 1911
}
}
- Everything nested is serialized according to the object structure.
- Deserialization back into Dictionary<string, Author> also works without issues.
5. Dictionary as part of another object
Dictionaries are often used as a field inside a more complex object. For example, a library has a catalog of books where each key is a genre name and the value is a list of books of that genre.
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>>
{
["Science Fiction"] = new List<Book>
{
new Book { Title = "Solaris", Author = "Stanislaw Lem" }
},
["Classics"] = new List<Book>
{
new Book { Title = "Lord of the Flies", Author = "William Golding" },
new Book { Title = "Darkness Visible", Author = "William Golding" }
}
}
};
string jsonLibrary = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonLibrary);
Fragment of output JSON:
{
"CatalogByGenre": {
"Science Fiction": [
{
"Title": "Solaris",
"Author": "Stanislaw Lem"
}
],
"Classics": [
{
"Title": "Lord of the Flies",
"Author": "William Golding"
},
{
"Title": "Darkness Visible",
"Author": "William Golding"
}
]
}
}
Everything works — nested dictionaries and collections are serialized and deserialized recursively.
6. Features and pitfalls of dictionary serialization
Duplicate keys
In a dictionary keys are always unique. But if you manually provide JSON with duplicate keys:
{
"foo": "first",
"foo": "second"
}
Result: the last value ("second") will overwrite the first, there will be no error. That's how most JSON parsers work.
Order of elements
A dictionary is an unordered collection. When serialized, the order of keys in JSON may differ from the original. If order matters — use a list of pairs (List<KeyValuePair<string, T>>), but usually key order doesn't matter for dictionaries.
JSON and nested dictionaries
Nesting levels are unlimited, but for correct behavior each level must comply with JSON constraints (keys — strings, values — valid JSON objects/arrays).
Using JsonSerializerOptions
Sometimes you need property names not in PascalCase but in camelCase. This is especially important if you're integrating with a JavaScript frontend where camelCase is the standard for field names.
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
string camelJson = JsonSerializer.Serialize(authorDirectory, options);
Important: for dictionaries this option affects only the serialization of nested objects (their properties), not dictionary keys. Dictionary keys are always serialized as the exact string that was specified in C#.
7. Problems with complex and non-string keys
Serialization of dictionaries with string and numeric keys (for example, int, long, Guid) works out of the box. But if you try to use a custom class or struct as a key — you'll get a NotSupportedException.
There are workarounds for serializing such cases:
- Use a different storage format, for example, serialize the dictionary as an array of objects with fields "Key" and "Value".
- Write a converter (JsonConverter) that converts your complex key to a string and back.
- If the structure is really complex — sometimes it's worth revisiting the architecture and not using complex objects as dictionary keys.
Workaround example by serializing as a list of pairs
public class AuthorInfo
{
public Author Author { get; set; }
public string Book { get; set; }
}
// instead of Dictionary<Author, string>
var list = new List<AuthorInfo>
{
new AuthorInfo { Author = new Author { Name = "William Golding", BirthYear = 1911 }, Book = "Lord of the Flies" }
};
// such a list serializes without problems
Comparison: dictionary vs. list of pairs when serializing
| Collection type | JSON structure | When to use |
|---|---|---|
|
|
Keys are simple strings, need fast lookup and uniqueness |
|
|
Key is a complex type, need control of order, duplicates possible |
8. Typical interview questions
1. Can you serialize Dictionary<DateTime, string>?
Yes, but keys are converted to string representation (usually ISO format like "yyyy-MM-ddTHH:mm:ss"). Sometimes deserialization can have issues with locales and date formats.
2. What happens if you serialize Dictionary<int, string>?
Keys will be serialized as strings even if the original dictionary had numbers. Deserialization back works normally.
3. Why can't you serialize a dictionary with objects as keys?
Only strings can be JSON object property names, objects cannot.
4. What if you want to serialize a dictionary with a complex key?
It's better to rethink the structure or serialize as a list of "key-value" pairs where the key is serialized fully as an object field rather than as a property name.
GO TO FULL VERSION