1. Introduction
Un dictionnaire (ou Dictionary<TKey, TValue> en C#) est une collection de paires "clé-valeur". Ce type de donnée est indispensable quand il faut retrouver rapidement une valeur par un identifiant unique (par exemple, chercher le numéro de téléphone par nom dans un annuaire).
Contrairement aux listes (List<T>), où l'ordre des éléments compte, le dictionnaire se concentre sur l'accès rapide par clé. Mais si la sérialisation d'une liste est simple (un tableau JSON), la sérialisation d'un dictionnaire apporte plusieurs subtilités :
- La clé doit être d'un type sérialisable (le plus souvent une chaîne, mais parfois un nombre ou même un autre objet).
- En JSON il n'y a pas de "dictionnaire" distinct — il y a des objets ou des tableaux.
Voyons en détail comment .NET sérialise les dictionnaires, les difficultés possibles et comment "enseigner" correctement notre code à gérer ces structures.
2. Sérialisation d'un dictionnaire avec des clés chaînes
On commence par le classique — un dictionnaire où la clé et la valeur sont des chaînes.
// Exemple de dictionnaire : livre et son auteur
var books = new Dictionary<string, string>
{
["Le Maître et Marguerite"] = "Mikhaïl Boulgakov",
["Harry Potter"] = "J. K. Rowling",
["Sa Majesté des mouches"] = "William Golding"
};
// Sérialisation en chaîne JSON
string json = JsonSerializer.Serialize(books, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine("Dictionnaire sérialisé en JSON :\n" + json);
// Enregistrer dans un fichier (synchronement)
File.WriteAllText("books.json", json);
Console.WriteLine("JSON écrit dans le fichier books.json.");
// Lire depuis le fichier (synchronement)
string jsonFromFile = File.ReadAllText("books.json");
// Désérialisation en dictionnaire
var restoredBooks = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonFromFile);
Console.WriteLine("Résultat de la désérialisation :");
foreach (var pair in restoredBooks)
Console.WriteLine($"{pair.Key} -> {pair.Value}");
Que contiendra le fichier books.json ?
{
"Le Maître et Marguerite": "Mikhaïl Boulgakov",
"Harry Potter": "J. K. Rowling",
"Guerre et Paix": "William Golding"
}
Comment ça marche ?
Le serializer transforme notre Dictionary<string, string> en objet JSON où chaque clé devient le nom d'une propriété et la valeur devient la valeur de cette propriété. C'est pratique si les clés sont des chaînes et qu'elles sont uniques.
3. Dictionnaire avec un type de clé non standard
C'est simple tant que la clé est une chaîne. Et si la clé est, par exemple, un nombre ?
var bookIds = new Dictionary<int, string>
{
[1001] = "Le Maître et Marguerite",
[1002] = "Harry Potter",
[1003] = "Sa Majesté des mouches"
};
string jsonIntKeys = JsonSerializer.Serialize(bookIds, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonIntKeys);
Résultat :
{
"1001": "Le Maître et Marguerite",
"1002": "Harry Potter",
"1003": "Sa Majesté des mouches"
}
Que s'est-il passé ?
- C# a converti les clés numériques en chaînes parce qu'en JSON les noms de propriétés doivent être des chaînes.
- Lors de la désérialisation vers Dictionary<int, string>, le serializer tentera de convertir la chaîne en nombre.
Exemple de désérialisation :
var restoredBookIds = JsonSerializer.Deserialize<Dictionary<int, string>>(jsonIntKeys);
// Tout fonctionne ! Les clés sont redevenues des nombres.
Et si la clé est un type complexe, par exemple un objet ?
var dict = new Dictionary<Author, string>
{
[new Author { Name = "Golding", BirthYear = 1911 }] = "Sa Majesté des mouches"
};
Tenter de sérialiser un tel dictionnaire entraînera une exception :
System.NotSupportedException: Serialization and deserialization of 'Dictionary<Author, string>' instances are not supported.
Pourquoi ?
Un objet JSON ne peut utiliser que des chaînes comme noms de propriétés. Donc, pour la sérialisation, les clés de dictionnaire doivent être des types simples, convertibles de manière univoque en chaîne (le plus souvent string ou number). Les objets complexes ne peuvent pas être utilisés comme clés pour la sérialisation JSON avec les outils standards.
4. Dictionnaire avec des objets imbriqués en tant que valeurs
Les clés sont réglées — voyons maintenant ce qui se passe si la valeur est un objet complexe (par exemple Book ou Author).
Exemple
public class Author
{
public string Name { get; set; }
public int BirthYear { get; set; }
}
var authorDirectory = new Dictionary<string, Author>
{
["bulgakov"] = new Author { Name = "Mikhaïl Boulgakov", BirthYear = 1891 },
["golding"] = new Author { Name = "William Golding", BirthYear = 1911 }
};
string jsonAuthors = JsonSerializer.Serialize(authorDirectory, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonAuthors);
Résultat :
{
"bulgakov": {
"Name": "Mikhaïl Boulgakov",
"BirthYear": 1891
},
"golding": {
"Name": "William Golding",
"BirthYear": 1911
}
}
- Tout ce qui est imbriqué est sérialisé selon la structure des objets.
- La désérialisation vers Dictionary<string, Author> fonctionne également sans problème.
5. Dictionnaire dans un objet plus grand
Très souvent les dictionnaires sont des champs d'objets plus complexes. Par exemple, une bibliothèque peut avoir un catalogue où chaque clé est le nom d'un genre et la valeur est une liste de livres de ce 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>>
{
["Fantastique"] = new List<Book>
{
new Book { Title = "Solaris", Author = "Stanisław Lem" }
},
["Classique"] = new List<Book>
{
new Book { Title = "Sa Majesté des mouches", Author = "William Golding" },
new Book { Title = "Ténèbres visibles", Author = "William Golding" }
}
}
};
string jsonLibrary = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonLibrary);
Extrait du JSON généré :
{
"CatalogByGenre": {
"Fantastique": [
{
"Title": "Solaris",
"Author": "Stanisław Lem"
}
],
"Classique": [
{
"Title": "Sa Majesté des mouches",
"Author": "William Golding"
},
{
"Title": "Ténèbres visibles",
"Author": "William Golding"
}
]
}
}
Tout fonctionne — les dictionnaires et collections imbriqués sont sérialisés et désérialisés récursivement.
6. Particularités et pièges de la sérialisation des dictionnaires
Clés dupliquées
Dans un dictionnaire, les clés sont toujours uniques. Mais si vous fournissez manuellement un JSON avec des clés dupliquées :
{
"foo": "first",
"foo": "second"
}
Résultat : la dernière valeur ("second") écrasera la première, il n'y aura pas d'erreur. C'est le comportement de la plupart des parseurs JSON.
Ordre des éléments
Le dictionnaire est une collection non ordonnée. À la sérialisation, l'ordre des clés dans le JSON peut différer de l'ordre initial. Si l'ordre est critique — utilisez une liste de paires (List<KeyValuePair<string, T>>), mais en général pour un dictionnaire l'ordre n'a pas d'importance.
JSON et dictionnaires imbriqués
Le niveau d'imbrication n'est pas limité, mais pour que tout fonctionne correctement chaque niveau doit respecter les contraintes du JSON (clés — chaînes, valeurs — objets/arrays JSON valides).
Utilisation de JsonSerializerOptions
Parfois on veut que les noms de propriétés ne soient pas en PascalCase mais en camelCase. C'est particulièrement important si vous vous intégrez avec un frontend JavaScript, où camelCase est souvent la convention.
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
string camelJson = JsonSerializer.Serialize(authorDirectory, options);
Important : pour les dictionnaires ce paramètre influence uniquement la sérialisation des objets imbriqués (leurs propriétés), pas les clés du dictionnaire. Les clés sont toujours sérialisées exactement telles qu'elles ont été indiquées en C#.
7. Problèmes avec des clés complexes et non-string
La sérialisation des dictionnaires avec des clés string et des clés numériques (par ex. int, long, Guid) fonctionne "out of the box". Mais si vous essayez d'utiliser comme clé une classe ou une struct personnalisée — vous obtiendrez une exception NotSupportedException.
Pour sérialiser ces cas il existe des contournements :
- Utiliser un autre format de stockage, par exemple sérialiser le dictionnaire comme un tableau d'objets avec des champs "Key" et "Value".
- Écrire un converter (JsonConverter) qui transforme votre clé complexe en chaîne et inversement.
- Si la structure est vraiment complexe — reconsidérer l'architecture et éviter d'utiliser des objets complexes comme clés.
Exemple de contournement : sérialiser comme liste de paires
public class AuthorInfo
{
public Author Author { get; set; }
public string Book { get; set; }
}
// au lieu de Dictionary<Author, string>
var list = new List<AuthorInfo>
{
new AuthorInfo { Author = new Author { Name = "William Golding", BirthYear = 1911 }, Book = "Sa Majesté des mouches" }
};
// une telle liste se sérialise sans problème
Comparaison : dictionnaire vs liste de paires pour la sérialisation
| Type de collection | Structure JSON | Quand l'utiliser |
|---|---|---|
|
|
Clés — chaînes simples, besoin de recherches rapides et d'unicité |
|
|
Clé — type complexe, besoin de contrôler l'ordre, duplicatas possibles |
8. Questions fréquentes en entretien
1. Peut-on sérialiser un Dictionary<DateTime, string> ?
Oui, mais les clés seront converties en représentation chaîne (généralement un format ISO du type "yyyy-MM-ddTHH:mm:ss"). Lors de la désérialisation des problèmes peuvent survenir à cause de locales et de formats de date.
2. Que se passe-t-il si on sérialise un Dictionary<int, string> ?
Les clés seront sérialisées comme des chaînes, même si dans le dictionnaire initial elles étaient des nombres. La désérialisation inverse se fait correctement.
3. Pourquoi ne peut-on pas sérialiser un dictionnaire avec des objets comme clés ?
Parce que seuls des strings peuvent être des noms de propriété d'un objet JSON, et un objet ne peut pas être un nom de propriété.
4. Et si on veut absolument sérialiser un dictionnaire avec une clé complexe ?
Mieux vaut revoir la structure ou sérialiser comme une liste de paires "key-value", où la clé est sérialisée entièrement comme un objet-champ, et non comme le nom d'une propriété.
GO TO FULL VERSION