En Rust, la sérialisation et la désérialisation des données sont des tâches assez courantes, surtout lors de l'intégration avec des services web, des bases de données ou l'échange de messages entre composants. La bibliothèque la plus populaire pour cela est serde, qui est devenue la norme de facto pour travailler avec la sérialisation en Rust.
Contexte de la question
Les développeurs Rust ont rencontré la nécessité d'une sérialisation flexible et performante. Initialement, il existait une multitude de solutions spécialisées, mais c'est serde qui a proposé une intégration pratique avec des macros derive, un schéma flexible de support pour divers formats (JSON, CBOR, BSON, TOML, YAML et d'autres).
Problème
Il est nécessaire de garantir une transformation correcte des structures Rust en formats d'échange de données (par exemple JSON), et vice versa, sans perdre la sécurité de type et sans permettre des "erreurs silencieuses". Les problèmes sont souvent causés par une incompatibilité de structure de données ou par la tentative de sérialiser des types non pris en charge.
Solution
Pour sérialiser des structures, il faut implémenter les traits Serialize et Deserialize, généralement à l'aide de #[derive(Serialize, Deserialize)]. Pour des cas non standards, on peut implémenter ces traits manuellement ou utiliser des attributs qui gèrent le schéma de sérialisation.
Exemple de code :
use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] struct Person { name: String, age: u8, } fn main() { let data = Person { name: "Bob".into(), age: 32 }; let json = serde_json::to_string(&data).unwrap(); println!("{}", json); let obj: Person = serde_json::from_str(&json).unwrap(); println!("{:?}", obj); }
Caractéristiques clés :
Comment sérialiser/désérialiser des champs Option ou des champs avec des valeurs par défaut ?
On peut utiliser #[serde(default)] pour indiquer une valeur par défaut ou #[serde(skip_serializing_if = "Option::is_none")], pour ne pas sérialiser None.
Exemple de code :
#[derive(Serialize, Deserialize)] struct Config { #[serde(default)] timeout: Option<u32>, }
Peut-on sérialiser des structures avec des champs personnalisés ou des valeurs calculées (par exemple fn get_hash()) ?
Oui, mais ces champs doivent être marqués avec #[serde(skip)], s'ils ne doivent pas être sérialisés, ou implémenter Serialize/Deserialize manuellement, si des valeurs calculées doivent être sérialisées.
Que se passe-t-il si la structure en Rust ne correspond pas aux champs de l'objet JSON (champ manquant ou nouveau) ?
Par défaut, serde ignore les champs inutiles dans JSON (sauf si le mode strict est activé), et renverra une erreur en cas d'absence de champs obligatoires dans la structure (si #[serde(default)] n'est pas spécifié).
Un JSON externe arrive avec des champs supplémentaires et manquants, et la structure n'utilise pas #[serde(default)],
Avantages :
Inconvénients :
Utilisation de #[serde(default)] et #[serde(skip_serializing_if)], tous les champs sont validés, les surplus sont ignorés.
Avantages :
Inconvénients :