CodeGym /Cours /C# SELF /Configuration de la sérialisation XML

Configuration de la sérialisation XML

C# SELF
Niveau 48 , Leçon 1
Disponible

1. Introduction

Vous savez déjà que .NET peut sérialiser et désérialiser des objets en XML par défaut, en se basant sur les champs publics et les propriétés. Mais quand on veut produire un XML propre, compatible avec des programmes/standards externes, ou quand on ne veut pas inclure certains détails internes dans le XML, il faut affiner la configuration.

En C#, il existe des attributs de sérialisation — des "étiquettes" qu’on applique aux classes, propriétés ou champs. Grâce à ces attributs on peut tout contrôler : quels champs vont dans le XML, lesquels seront ignorés, comment un élément s’appelle, devenir un attribut, avoir une structure complexe et même l’ordre des éléments !

Imaginez que la sérialisation par défaut est comme remplir automatiquement un formulaire : nom-prénom-patronyme dans leurs cases, le reste au hasard. Avec les attributs vous êtes comme un super-fonctionnaire de l’administration : vous décidez quelle ligne va où, quoi sauter, quoi surligner en rouge, et quoi cacher dans une section secrète.

Mapping entre objet et XML

Classe/Propriété Attribut Résultat en XML
[XmlRoot("Persons")]
Classe UserList
<Persons>...</Persons>
List<User> Users
[XmlElement("Person")]
<Person>...</Person>
int Id
[XmlAttribute("id")]
id="..." (attribut)
string Name
[XmlElement("FullName")]
<FullName>...</FullName>
string[] Emails
[XmlArray("EMails")][XmlArrayItem("Email")]
<EMails><Email>...</Email></EMails>
DateTime RegisteredAt
[XmlIgnore] (voir RegisteredAtString) -
string RegisteredAtString
[XmlAttribute("registered")]
registered="..."
Address[] Addresses
[XmlArray("UserAddresses")][XmlArrayItem("Address")]
<UserAddresses>...</UserAddresses>

2. Vue d'ensemble des principaux attributs de sérialisation XML

Parcourons les attributs les plus populaires que les développeurs .NET utilisent depuis des générations !

Attribut [XmlElement] — Nom de l'élément

Change le nom de l'élément dans le XML ou marque la propriété/champ comme un XML-element.

public class Person
{
    [XmlElement("FullName")]
    public string Name { get; set; }
}

XML :

<Person>
  <FullName>Vassili Petrov</FullName>
</Person>

Si vous ne mettez pas l'attribut — par défaut le nom de l'élément sera le nom de la propriété/champ.

Attribut [XmlAttribute] — Sérialiser en attribut XML

Permet de sérialiser une propriété/champ comme un attribut XML, au lieu d'un élément.

public class Person
{
    [XmlAttribute("id")]
    public int Id { get; set; }
}

XML :

<Person id="123"></Person>

Dans de vrais API XML on utilise souvent les attributs pour les identifiants, dates, flags, etc.

Attribut [XmlIgnore] — Ignorer un champ/propriété

Le rêve de tout paranoïaque : exclure complètement une propriété du processus de sérialisation et de désérialisation ! Tout ce qui est marqué avec cet attribut ne figurera tout simplement pas dans le XML final.

public class Person
{
    [XmlIgnore]
    public string InternalNote { get; set; }
}

XML : (la propriété InternalNote n'est pas présente)

Attributs [XmlArray] et [XmlArrayItem] — contrôle des collections

Attributs spéciaux pour les tableaux et collections. [XmlArray] fixe le nom de l'enveloppe extérieure, et [XmlArrayItem] le nom de chaque élément.

public class Person
{
    [XmlArray("Phones")]
    [XmlArrayItem("Phone")]
    public string[] PhoneNumbers { get; set; }
}

XML :

<Person>
  <Phones>
    <Phone>+12951234567</Phone>
    <Phone>+12876543210</Phone>
  </Phones>
</Person>

Attribut [XmlRoot] — Nom de l'élément racine

Marque la classe elle-même pour changer le nom de l'élément racine dans le XML.

[XmlRoot("User")]
public class Person
{
    public string Name { get; set; }
}

XML :

<User>
  <Name>Anna</Name>
</User>

Attribut [XmlText] — Sérialiser dans le contenu texte d'un élément

Parfois (ou selon la spec) on veut que la valeur d'une propriété soit juste le texte à l'intérieur de l'élément, pas un sous-élément ou un attribut.

public class Note
{
    [XmlText]
    public string Content { get; set; }
}

XML :

<Note>Appelle grand-mère !</Note>

Attributs [XmlNamespaceDeclarations], [XmlElement(Type = ...)]

Pour des scénarios avancés (par ex. sérialisation avec namespaces, support de l'héritage, etc.), il existe des attributs plus spécifiques. Consultez la documentation officielle sur la sérialisation XML pour la liste complète.

3. Pratique : améliorer la sérialisation sur un exemple réel

Allons pas à pas pour upgrader notre application pédagogique vers une "sérialisation XML premium" qui ferait plaisir au plus strict des inspecteurs XML.

Classe de base sans attributs

Supposons qu'on ait cette classe utilisateur :

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string[] Emails { get; set; }
    public DateTime RegisteredAt { get; set; }
    public string Password { get; set; }
}

La sérialisation par défaut donnera à peu près ce XML (la date sera aussi au format par défaut) :

<User>
  <Id>42</Id>
  <Name>Ivan Ivanov</Name>
  <Emails>
    <string>user@mail.com</string>
    <string>admin@site.org</string>
  </Emails>
  <RegisteredAt>2024-07-01T00:00:00</RegisteredAt>
  <Password>qwerty</Password>
</User>

Ce qui cloche :

  • Mieux vaut ne pas sérialiser le mot de passe du tout.
  • On veut la date comme attribut et dans un format lisible.
  • On veut les Emails sous une forme plus jolie.
  • On veut l'élément racine <Person>.

Ajout des réglages via attributs

[XmlRoot("Person")]
public class User
{
    [XmlAttribute("id")]
    public int Id { get; set; }

    [XmlElement("FullName")]
    public string Name { get; set; }

    [XmlArray("EMails")]
    [XmlArrayItem("Email")]
    public string[] Emails { get; set; }

    [XmlAttribute("registered")]
    public string RegisteredAtString
    {
        get => RegisteredAt.ToString("yyyy-MM-dd");
        set => RegisteredAt = DateTime.Parse(value);
    }

    [XmlIgnore]
    public string Password { get; set; }

    [XmlIgnore]
    public DateTime RegisteredAt { get; set; }
}

Explication des subtilités :

  • On utilise [XmlAttribute("registered")] pour sérialiser la date comme attribut.
  • Pour ne pas sérialiser directement le DateTime, mais sa représentation en chaîne, on ajoute une propriété séparée RegisteredAtString, et on cache le champ réel via [XmlIgnore].
  • On cache le mot de passe avec [XmlIgnore].
  • Le tableau d'emails est configuré pour avoir la balise externe <EMails> et chaque email en <Email>.

Résultat :

<Person id="42" registered="2024-07-01">
  <FullName>Ivan Ivanov</FullName>
  <EMails>
    <Email>user@mail.com</Email>
    <Email>admin@site.org</Email>
  </EMails>
</Person>

4. Travailler avec des objets imbriqués

Le XML brille quand il y a des objets imbriqués (par ex. un utilisateur et ses adresses).

public class Address
{
    [XmlAttribute]
    public string City { get; set; }

    [XmlText]
    public string Details { get; set; }
}

public class User
{
    // ...autres propriétés, voir plus haut...

    public Address[] Addresses { get; set; }
}

Ajoutons la configuration pour le tableau d'adresses :

[XmlArray("UserAddresses")]
[XmlArrayItem("Address")]
public Address[] Addresses { get; set; }

Alors le XML ressemblera à ceci :

<Person id="42" registered="2024-07-01">
  <FullName>Ivan Ivanov</FullName>
  <EMails>
    <Email>user@mail.com</Email>
  </EMails>
  <UserAddresses>
    <Address City="Neon city">Zamkovaya St. 1</Address>
    <Address City="North Cave">Krajnyaya St. 12</Address>
  </UserAddresses>
</Person>

On voit que pour chaque <Address> city est un attribut, et l'adresse elle-même est le texte à l'intérieur.

5. Erreurs fréquentes et subtilités

Si vous sérialisez un objet complexe et ne voyez pas les changements attendus dans le XML — probablement vous avez oublié d'ajouter correctement l'attribut. Parfois des propriétés/champs restent sans marquage. Alors .NET applique le comportement par défaut.

Autre piège — le nom de la collection. Si une collection (par ex. tableau de strings) n'est marquée ni par [XmlArray] ni par [XmlArrayItem], les éléments seront écrits avec la balise string (comme dans l'exemple initial ci-dessus). Pour éviter ça, donnez toujours explicitement les noms de balises via les attributs.

Faites attention aux propriétés en lecture seule (getter-only) — elles ne se désérialisent pas par défaut. Essayez d'avoir un set public quand c'est nécessaire.

Certains types (par ex. dictionnaires ou interfaces) ne se sérialisent pas avec le XmlSerializer standard, ou demandent un traitement spécial.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION