CodeGym /Cours /C# SELF /Gestion de la sérialisation des collections

Gestion de la sérialisation des collections

C# SELF
Niveau 46 , Leçon 4
Disponible

1. Introduction

Autant on aimerait penser que les collections se sérialisent et se désérialisent parfaitement “out of the box”, dans de vrais projets ce n'est souvent pas le cas. Parfois il faut cacher certaines collections de la sérialisation — par exemple des données mises en cache en interne. Parfois il faut renommer les propriétés de collection pour qu'elles correspondent au contrat API. Dans certains cas il est important de contrôler quels éléments sont sauvegardés ou ignorés, ou même de transformer la collection d'une façon spéciale pour que le JSON final soit compréhensible et “lisible” par d'autres services.

Heureusement, System.Text.Json offre une manière simple et transparente de contrôler la sérialisation via des attributs qu'on peut appliquer aussi bien aux collections qu'aux éléments individuels. Dans cette section on va continuer à développer notre modèle de bibliothèque pour voir comment ça marche en pratique.

2. Exclure des propriétés de collection : [JsonIgnore]

Commençons par le simple. Parfois dans votre classe il y a une collection qu'on ne doit pas sérialiser — par exemple des données temporaires, mises en cache ou sensibles. Que faire ? Bien sûr, [JsonIgnore] !

Imaginons que nous avons une classe Library dans laquelle nous avons ajouté une propriété List<Book> Cache, utilisée seulement pour l'accès rapide :

using System.Text.Json.Serialization;

public class Library
{
    public string Name { get; set; }

    public List<Book> Books { get; set; }

    [JsonIgnore]
    public List<Book> Cache { get; set; } // Ne se sérialise pas !
}
// Exemple d'utilisation :
var library = new Library
{
    Name = "Bibliothèque principale",
    Books = new List<Book>
    {
        new Book { Title = "La vallée magique", Author = new Author { Name = "Tove Jansson", BirthYear = 1914 } }
    },
    Cache = new List<Book>
    {
        new Book { Title = "Sa Majesté des mouches", Author = new Author { Name = "William Golding", BirthYear = 1911 } }
    }
};

string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json); // Il n'y a pas la propriété Cache dans le JSON !

Le résultat de la sérialisation sera à peu près comme ceci :

{
  "Name": "Bibliothèque principale",
  "Books": [
    {
      "Title": "La vallée magique",
      "Author": {
        "Name": "Tove Jansson",
        "BirthYear": 1914
      }
    }
  ]
}

Vous voyez ? Aucun “cache” dans le monde extérieur. Tout ce qui est sous [JsonIgnore] est caché et sûr, comme le mot de passe Wi-Fi dans votre tête.

3. Renommer les collections avec [JsonPropertyName]

Vous tombez souvent sur des API qui attendent, par exemple, "items" au lieu de "Books" ? Ou bien vous ne voulez pas renommer le champ en C# (pour ne pas vous embrouiller), mais dans le JSON il doit “sonner” différemment ?

Voilà comment on fait :

using System.Text.Json.Serialization;

public class Library
{
    public string Name { get; set; }

    [JsonPropertyName("items")]
    public List<Book> Books { get; set; }

    [JsonIgnore]
    public List<Book> Cache { get; set; }
}
// Sérialisation :
var library = new Library { Name = "Succursale n°1", Books = new List<Book>() };
string json = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

Sortie :

{
  "Name": "Succursale n°1",
  "items": []
}

Notez que la désérialisation associera aussi correctement la propriété items du JSON à Books en C# — la magie fonctionne dans les deux sens.

4. Contrôler la sérialisation des collections et de leurs éléments

Pour cela vous aurez besoin de JsonIgnoreCondition.WhenWritingNull et/ou des types nullable.

Il arrive qu'une collection soit simplement un champ optionnel. Par exemple, une bibliothèque fraîchement créée n'a pas encore de livres. Si vous ne voulez pas que le JSON contienne la propriété books: null, vous pouvez le contrôler via les options :

var library = new Library { Name = "Bibliothèque vide" };
// Books n'est pas initialisé = null

var options = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    WriteIndented = true
};

string json = JsonSerializer.Serialize(library, options);
Console.WriteLine(json);

Résultat :

{
  "Name": "Bibliothèque vide"
}

Et si vous avez une liste vide (mais non null), le sérialiseur produira "books": []. C'est une distinction importante, parce que parfois on veut volontairement cacher le champ s'il est null, mais pas une liste vide.

5. Attribut [JsonIgnore] sur les propriétés des éléments

Les attributs de sérialisation fonctionnent aussi à l'intérieur des éléments d'une collection. Vous pouvez cacher des propriétés individuelles de chaque objet dans la liste.

public class Book
{
    public string Title { get; set; }
    public Author Author { get; set; }

    [JsonIgnore]
    public string InternalCode { get; set; }
}

Maintenant, lors de la sérialisation d'un livre dans la collection Books, la propriété InternalCode n'apparaîtra pas dans le JSON.

6. Adresser des collections via “index” ou structures imbriquées

Parfois il faut sérialiser des collections non pas comme des tableaux, mais, par exemple, comme des “maps” (dictionary) — si chaque livre a un identifiant unique. Dans ce cas — pas besoin d'attributs tordus pour les éléments, mais avec les moyens standards — on peut déclarer une propriété dictionnaire :

public class Library
{
    [JsonPropertyName("catalog")]
    public Dictionary<string, Book> BookCatalog { get; set; }
}

En sérialisant, le dictionnaire deviendra un objet avec des paires clé-valeur :

var library = new Library
{
    BookCatalog = new Dictionary<string, Book>
    {
        ["978-5-699-12345-6"] = new Book { Title = "Dictionnaire", Author = new Author { Name = "Inconnu", BirthYear = 2000 } }
    }
};

JSON :

{
  "catalog": {
    "978-5-699-12345-6": {
      "Title": "Dictionnaire",
      "Author": {
        "Name": "Inconnu",
        "BirthYear": 2000
      }
    }
  }
}

Cette représentation est pratique pour l'échange via API où il est important de conserver la liaison entre la clé et l'objet.

7. Erreurs et difficultés en gérant la sérialisation des collections

Si vous tentez de sérialiser une collection dans laquelle tous les éléments ne sont pas correctement initialisés (par exemple la liste contient des null), par défaut System.Text.Json écrira ces éléments comme null dans le tableau.

Même si vous avez défini DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, les éléments null à l'intérieur du tableau resteront — la règle concerne les propriétés d'objet, pas le contenu des collections. Pour éviter ça, nettoyez la collection au préalable : RemoveAll(b => b == null).

Une confusion fréquente à la désérialisation est la non-correspondance des noms. Si vous oubliez de préciser [JsonPropertyName], la classe attendra la propriété Books, et si vous envoyez le JSON avec items, la collection ne sera pas remplie et restera vide. Vérifiez toujours l'exactitude des noms !

8. Tableau : où s'appliquent les principaux attributs

Attribut Peut-on l'appliquer aux collections ? Peut-on l'appliquer aux éléments des collections ? Exemples d'utilisation
[JsonIgnore]
oui oui Cacher une liste ou un champ à l'intérieur de Book
[JsonPropertyName]
oui oui Renommer Books → items ou Title → name
[JsonInclude]
oui oui Inclure des propriétés privées dans la sérialisation
[JsonConverter]
oui oui Attribuer un converter spécial à une liste

9. Schéma de sérialisation des collections avec attributs


+-------------+
|   Library   |
+-------------+
  | Name           -- sérialisé comme "Name"
  | Books          -- [JsonPropertyName("items")], sérialisé comme "items": [...]
  | Cache          -- [JsonIgnore], ne se sérialise pas
  | BookCatalog    -- [JsonPropertyName("catalog")], sérialisé comme "catalog": {...}
Le résultat JSON est à peu près ceci :
{
  "Name": "Bibliothèque municipale",
  "items": [
    {
      "Title": "1984",
      "Author": {
        "Name": "George Orwell",
        "BirthYear": 1903
      }
    },
    {
      "Title": "Les grandes espérances",
      "Author": {
        "Name": "Charles Dickens",
        "BirthYear": 1812
      }
    }
  ],
  "catalog": {
    "978-1234567890": {
      "Title": "L'appel de Cthulhu",
      "Author": {
        "Name": "Howard Phillips Lovecraft",
        "BirthYear": 1890
      }
    }
  }
}

10. Valeur pratique et particularités en entretien et en projets réels

En conditions “réelles” il faut toujours tenir compte du contrat de l'API externe et des exigences de sérialisation. Il faut savoir “cacher” les collections internes, respecter la casse et le style des noms, et parfois même changer dynamiquement le schéma de sérialisation selon la version du client.

Questions typiques en entretien :

  • Comment sérialiser seulement une partie des données ?
  • Comment faire pour qu'une propriété de collection n'apparaisse pas dans le JSON ?
  • Comment faire correspondre les noms des propriétés en C# et en JSON s'ils diffèrent ?
  • Peut-on cacher des éléments individuels à l'intérieur d'une collection (par ex. des informations confidentielles) ?

Les réponses tournent autour d'une utilisation avisée des attributs et des paramètres de sérialisation : [JsonIgnore], [JsonPropertyName], les options JsonSerializerOptions et un travail réfléchi sur le contenu des collections.

2
Mission
C# SELF, niveau 46, leçon 4
Bloqué
Renommage de la propriété d'une collection
Renommage de la propriété d'une collection
1
Étude/Quiz
Sérialisation des collections, niveau 46, leçon 4
Indisponible
Sérialisation des collections
Sérialisation des objets imbriqués et hiérarchiques
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION