1. Introduction
XML (eXtensible Markup Language) — un langage de balisage extensible, un format populaire pour stocker et échanger des données structurées complexes. Contrairement à JSON, XML n'est pas très compact, mais il est encore largement utilisé dans le domaine bancaire, les échanges entre services publics, les documents et les intégrations avec 1C, SAP, Oracle, etc. Certaines données sous Windows et .NET (par ex. les configs) sont toujours stockées en XML.
Visuellement, le XML ressemble au HTML, sauf qu'il n'y a pas de jeu fixe de balises comme <body> ou <div> — tu définis toi-même la structure des éléments. Exemple :
<Person>
<FirstName>Ivan</FirstName>
<LastName>Petrov</LastName>
<Age>30</Age>
</Person>
Bases du XML et correspondance avec les objets C#
À la différence de JSON, XML n'a pas de types dédiés pour les tableaux ou les booléens — tout est représenté par des éléments et des attributs. Les structures de liste sont réalisées comme des éléments répétés portant le même nom.
Pour travailler confortablement avec XML en C#, on utilise le mécanisme de sérialisation/désérialisation via XmlSerializer.
Sérialisation — conversion d'un objet C# en chaîne XML. Désérialisation — processus inverse : obtenir un objet C# depuis du XML.
Ça fonctionne "out of the box" si tes classes respectent certaines exigences — on verra ça ci‑dessous.
Pourquoi utiliser XmlSerializer ?
XmlSerializer — l'outil standard .NET (namespace System.Xml.Serialization) pour convertir des objets en XML et inversement. Il est simple, typesafe et adapté pour l'import/export de données, les configs, les échanges entre systèmes et les intégrations avec des services publics.
2. Exemple : sérialisation d'un objet
Imaginons qu'on ait le modèle utilisateur :
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Sérialisons l'objet en XML :
using System.Xml.Serialization;
User user = new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 };
// Pour la sérialisation on a besoin du type
XmlSerializer serializer = new XmlSerializer(typeof(User));
// On écrit le XML dans un fichier
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);
Que se passe-t-il ?
- On crée un objet de la classe User.
- On crée une instance de XmlSerializer pour le type User.
- On ouvre le fichier via FileStream.
- On appelle la méthode Serialize(), qui transforme l'objet en XML et l'écrit dans le fichier.
Contenu du fichier user.xml :
<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>Ivan</FirstName>
<LastName>Petrov</LastName>
<Age>30</Age>
</User>
Simple comme bonjour — peu de code et un XML prêt à l'emploi.
3. Désérialisation : lire le XML et récupérer l'objet
Lis le XML créé et restaure l'objet C# :
// On ouvre le fichier XML
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, âge : {loadedUser?.Age}");
La méthode Deserialize() retourne un object, donc on caste en User (avec as). Si le fichier est valide et correspond au modèle — l'objet est restauré complètement.
4. Comment fonctionne la sérialisation : particularités typiques
La sérialisation en XML demande de respecter certaines règles :
• La classe à sérialiser doit avoir un constructeur public par défaut (public sans paramètres).
• Les propriétés sérialisables sont publiques : il faut le getter et le setter. Les membres privés sont ignorés.
• Ne fonctionnent pas les propriétés en lecture seule, ni les propriétés static ou abstract.
Si tu enfreins ces règles, tu obtiendras une InvalidOperationException avec un message du genre "XmlSerializer cannot serialize this type".
5. Sérialisation des collections : tableaux et listes
Si tu dois sérialiser plusieurs utilisateurs — utilise une classe enveloppante :
public class UserList
{
public List<User> Users { get; set; } = new List<User>();
}
Créons la collection et sérialisons :
UserList users = new UserList
{
Users = new List<User>
{
new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 },
new User { FirstName = "Marie", LastName = "Ivanova", Age = 25 }
}
};
XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);
On obtient le XML :
<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Users>
<User>
<FirstName>Ivan</FirstName>
<LastName>Petrov</LastName>
<Age>30</Age>
</User>
<User>
<FirstName>Marie</FirstName>
<LastName>Ivanova</LastName>
<Age>25</Age>
</User>
</Users>
</UserList>
Peut-on sérialiser directement List<User> ? Oui, mais l'élément racine s'appellera ArrayOfUser, ce qui ne convient pas toujours pour les intégrations.
6. Attributs pour contrôler la sérialisation
Avec des attributs tu peux contrôler le format XML : renommer des éléments, sérialiser en attributs, ignorer des champs, etc. Attributs utiles : [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore].
Sérialiser comme attributs
On écrit le prénom et le nom comme attributs, et l'âge comme élément :
public class User
{
[XmlAttribute]
public string FirstName { get; set; }
[XmlAttribute]
public string LastName { get; set; }
[XmlElement]
public int Age { get; set; }
}
Résultat :
<User FirstName="Ivan" LastName="Petrov">
<Age>30</Age>
</User>
Contrôler les noms des éléments
Renommons les balises en quelque chose de lisible :
public class User
{
[XmlElement("Prénom")]
public string FirstName { get; set; }
[XmlElement("Nom")]
public string LastName { get; set; }
[XmlElement("Âge")]
public int Age { get; set; }
}
XML résultant :
<User>
<Prénom>Ivan</Prénom>
<Nom>Petrov</Nom>
<Âge>30</Âge>
</User>
Ignorer des propriétés
Si une propriété ne doit pas être sérialisée — marque-la avec [XmlIgnore] :
public class User
{
public string FirstName { get; set; }
[XmlIgnore]
public string InternalCode { get; set; }
}
7. Sérialisation de hiérarchies et d'objets imbriqués
Les classes imbriquées se transforment facilement en éléments XML imbriqués :
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class User
{
public string FirstName { get; set; }
public Address Address { get; set; }
}
var user = new User
{
FirstName = "Anna",
Address = new Address { City = "Neongrad", Street = "Vyazovaya" }
};
XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);
XML :
<User>
<FirstName>Anna</FirstName>
<Address>
<City>Neongrad</City>
<Street>Vyazovaya</Street>
</Address>
</User>
8. Sérialisation vers / depuis une chaîne (sans fichiers)
Sérialisation dans une chaîne
using System.Text;
var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();
serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);
Désérialisation depuis une chaîne
var xml = "<User><FirstName>Anna</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);
9. Exemple : export et import de données de l'application
Export/import d'une liste d'utilisateurs vers un fichier XML :
public static void ExportUsers(UserList users, string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Create);
serializer.Serialize(fs, users);
}
public static UserList ImportUsers(string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Open);
return (UserList)serializer.Deserialize(fs);
}
Usage : sauvegarde, échange avec d'autres services, intégrations. Aux entretiens on pose souvent des questions sur XmlSerializer — surtout dans les boîtes qui bossent avec des services publics et des banques.
10. Erreurs typiques et particularités
Si un champ n'est pas public, il n'est pas sérialisé.
Les propriétés en lecture seule sont ignorées.
Les propriétés dont le type est une interface ou une classe abstraite ne sont pas supportées par le serializer.
Le type DateTime est sérialisé au format XSD (voir la documentation).
Les collections comme les tableaux et List<T> fonctionnent de façon prévisible, alors qu'avec ObservableCollection<T> il peut y avoir des surprises chez des systèmes tiers.
Subtilités d'encodage : si tu écris via StreamWriter sans préciser l'encodage, le résultat peut être en UTF-16 ; pour la compatibilité on attend souvent du UTF-8 — précise l'encodage explicitement.
GO TO FULL VERSION