1. À quoi servent les attributs lors de la sérialisation ?
Quand vous sérialisez des objets en JSON ou XML, c'est un peu comme si un robot-aspirateur inspectait votre chambre. Tout ce qui est clairement visible (propriétés/champs publics) finit dans le "sac" (le fichier ou la chaîne), et le reste est ignoré. Mais parfois vous ne voulez pas que le robot montre discrètement le contenu de votre sac, ou vous voulez renommer des trucs — par exemple, pas en russe mais en anglais.
C'est là que les attributs entrent en jeu ! Ils permettent de contrôler le processus de sérialisation : cacher l'inutile, changer les noms, mettre un ordre, ignorer des parties de l'objet ou dire au serializer que vous avez des règles particulières. C'est comme des étiquettes sur les objets : «ne pas toucher», «important», «renommer en JSON».
Tâches typiques de contrôle de la sérialisation
- Changer le nom d'une propriété ou d'un champ dans le document de sortie (par exemple, en JSON au lieu de FirstName mettre "first_name")
- Cacher certains champs/propriétés de la sérialisation ou de la désérialisation (par exemple, mots de passe, compteurs internes)
- Gérer le traitement des valeurs par défaut ou des null-valeurs
- Définir l'ordre des éléments (pertinent pour XML)
- Décrire des paramètres supplémentaires (attributs) pour les éléments XML
Chaque plateforme de sérialisation — que ce soit System.Text.Json, Newtonsoft.Json ou XmlSerializer — utilise ses propres attributs pour ces objectifs.
2. Attributs pour System.Text.Json : moderne et tendance
Le serializer JSON classique du système .NET propose une belle liste d'attributs qui vivent dans l'espace de noms System.Text.Json.Serialization.
Les attributs les plus utiles :
| Attribut | À quoi ça sert | Exemple d'utilisation |
|---|---|---|
|
Transforme le nom de la propriété dans le JSON | |
|
Exclut complètement la propriété de la sérialisation | |
|
Sérialise un champ public (et pas seulement une propriété) | |
|
Ignorer la propriété selon certaines conditions | |
Exemples d'utilisation :
using System.Text.Json.Serialization;
public class Person
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; } // Le nom dans le JSON sera 'first_name'
[JsonIgnore]
public string Password { get; set; } // N'apparaitra pas dans le JSON
[JsonPropertyName("born_year")]
public int? YearOfBirth { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Nickname { get; set; } // Si null — n'apparaitra pas dans le JSON
}
Sérialisons cette personne :
var person = new Person
{
FirstName = "Ivan",
Password = "123456",
YearOfBirth = 2000,
Nickname = null
};
string json = JsonSerializer.Serialize(person);
// json: {"first_name":"Ivan","born_year":2000}
Remarque : le mot de passe et le surnom ont disparu de la base JSON ! (Le mot de passe — parce qu'il est toujours ignoré, et le surnom — seulement s'il est null).
Pourquoi c'est important ? En pratique, vous ne voulez souvent pas que le côté client (ou un attaquant !) voie des données sensibles comme les mots de passe, tokens, compteurs internes, timestamps internes. Avec [JsonIgnore] et des attributs similaires, on le fait en une ligne.
3. Attributs pour Newtonsoft.Json : le champion de la flexibilité
Si vous travaillez avec Newtonsoft.Json (doc ici), vous aurez encore plus d'options (et c'est parfois plus simple si vous venez de vieux projets).
Attributs principaux :
| Attribut | But |
|---|---|
|
Donne le nom de la propriété en JSON, ainsi que l'ordre, l'obligatoire, etc. |
|
Exclut le champ/propriété de la sérialisation |
|
Requiert la présence lors de la désérialisation |
|
Permet de spécifier un converter personnalisé pour des cas complexes |
|
Gère la sérialisation des valeurs par défaut |
Exemple pratique :
using Newtonsoft.Json;
public class UserProfile
{
[JsonProperty("login")]
public string Username { get; set; }
[JsonIgnore]
public string InternalNotes { get; set; }
[JsonProperty(Required = Required.Always)]
public string Email { get; set; }
}
Les possibilités supplémentaires de JsonProperty — exigence (Required), ordre (Order) etc. Par exemple :
[JsonProperty("id", Order = 1, Required = Required.Always)]
public int Id { get; set; }
4. Attributs pour XML : style "classique"
XmlSerializer utilise tout un arsenal d'attributs depuis l'espace de noms System.Xml.Serialization.
Les plus courants :
| Attribut | À quoi ça sert |
|---|---|
|
Élément dans le XML, avec un nom différent |
|
Transforme la propriété en attribut de l'élément XML |
|
Exclut la propriété ou le champ de la sérialisation XML |
|
Pour les collections — définit le nom du tableau XML |
|
Pour les collections — définit le nom de l'élément du tableau |
|
Change le nom du tag racine XML |
Exemple pratique :
using System.Xml.Serialization;
[XmlRoot("human")]
public class Person
{
[XmlElement("firstname")]
public string Name { get; set; }
[XmlAttribute("years")]
public int Age { get; set; }
[XmlIgnore]
public string Secret { get; set; }
}
var person = new Person { Name = "Anna", Age = 32, Secret = "42" };
Après la sérialisation, le XML ressemblera à peu près à ceci :
<human years="32"><firstname>Anna</firstname></human>
Notez que le champ Secret n'est pas dans le XML, et que Age est sérialisé comme attribut, pas comme un tag imbriqué.
5. Subtilités utiles
Sous le capot : comment les attributs fonctionnent
Quand le serializer rencontre votre classe, il la "lit" littéralement via la réflexion (magie .NET, voir System.Reflection). Le serializer lit les métadonnées : par exemple, est-ce que la propriété a l'attribut JsonIgnore ou XmlElement. Selon ça, il ajoute les données dans le document final (ou au contraire, les ignore).
C'est un moyen pratique de séparer le "schéma des données" de la logique métier. Votre classe — c'est votre logique métier, et les attributs — en gros, le passeport pour la sérialisation.
Table de correspondance des attributs clés
| Fonction | System.Text.Json | Newtonsoft.Json | XmlSerializer |
|---|---|---|---|
| Changer le nom | |
|
|
| Ignorer | |
|
|
| Format personnalisé | |
|
— (via IXmlSerializable, douloureux) |
| Racine de l'objet | — | — | |
| Collection | — | — | |
6. Scénarios avancés
Parfois vous devez sérialiser un objet d'une façon que le serializer standard ne gère pas. Par exemple, vous voulez stocker une date au format UNIX timestamp, pas en ISO standard. Pour ce cas il y a des attributs qui permettent de brancher des converters personnalisés.
Dans System.Text.Json :
public class UnixDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()).UtcDateTime;
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
=> writer.WriteNumberValue(new DateTimeOffset(value).ToUnixTimeSeconds());
}
public class LogEntry
{
[JsonConverter(typeof(UnixDateTimeConverter))]
public DateTime EventTime { get; set; }
}
Maintenant lors de la sérialisation, le champ EventTime deviendra un nombre, pas une chaîne de date.
Dans Newtonsoft.Json :
public class BoolToYesNoConverter : JsonConverter<bool>
{
public override void WriteJson(JsonWriter writer, bool value, JsonSerializer serializer)
=> writer.WriteValue(value ? "yes" : "no");
public override bool ReadJson(JsonReader reader, Type objectType, bool existingValue, bool hasExistingValue, JsonSerializer serializer)
=> (string)reader.Value == "yes";
}
public class Answer
{
[JsonConverter(typeof(BoolToYesNoConverter))]
public bool IsCorrect { get; set; }
}
Maintenant la valeur booléenne est sérialisée en "yes" ou "no", et redevient true ou false en lecture.
7. Particularités et pièges
Quand vous ajoutez des attributs, souvenez-vous : différents serializers utilisent différents attributs. Si vous travaillez avec JSON et XML (ou, au pire, simultanément avec Newtonsoft.Json et System.Text.Json), pensez à mettre les deux attributs nécessaires, sinon surprise.
Le nom standard de la propriété sera pris par le serializer, à moins que vous ne le redéfinissiez via un attribut.
Faites attention à l'héritage : les classes dérivées héritent des champs/propriétés publics et, si la classe parente avait un attribut, il s'appliquera aussi à la classe dérivée. Ça surprend souvent, mais c'est voulu.
Erreur fréquente : On voit souvent des développeurs qui marquent accidentellement un champ qui doit absolument être sérialisé avec l'attribut JsonIgnore (ou inversement — oublient d'ignorer des données sensibles). Ou par exemple, mettent XmlElement sur un champ privé — et se demandent pourquoi le serializer ne le voit pas (XmlSerializer n'opère que sur les membres public !).
GO TO FULL VERSION