In Rust zijn serialisatie en deserialisatie van gegevens vrij veel voorkomende taken, vooral bij integratie met webservices, databases of berichtenuitwisseling tussen componenten. De meest populaire bibliotheek hiervoor is serde, die de facto de standaard is geworden voor het werken met serialisatie in Rust.
Achtergrond van de vraag
Rust-ontwikkelaars stuitten op de noodzaak voor flexibele en efficiënte serialisatie. Aanvankelijk waren er veel gespecialiseerde oplossingen, maar serde bood een handige integratie met derive-macro's en een flexibele ondersteuning voor verschillende formats (JSON, CBOR, BSON, TOML, YAML en anderen).
Probleem
Het is noodzakelijk om een correcte transformatie van Rust-structs naar data-uitwisselingsformaten (bijvoorbeeld JSON) te garanderen, en vice versa, zonder typeveiligheid te verliezen en "stille fouten" (silent fail) te voorkomen. Problemen ontstaan vaak door een mismatch in de datavormen of pogingen om niet-ondersteunde types te serialiseren.
Oplossing
Voor het serialiseren van structs moet je de traits Serialize en Deserialize implementeren, meestal met behulp van #[derive(Serialize, Deserialize)]. Voor niet-standaard gevallen kun je deze traits handmatig implementeren of gebruik maken van attributen die de serialisatieschema's aansturen.
Codevoorbeeld:
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); }
Belangrijke kenmerken:
Hoe serialiseer/deserialiseer je Option-velden of velden met standaardwaarden?
Je kunt #[serde(default)] gebruiken om een standaardwaarde aan te geven of #[serde(skip_serializing_if = "Option::is_none")] om None niet te serialiseren.
Codevoorbeeld:
#[derive(Serialize, Deserialize)] struct Config { #[serde(default)] timeout: Option<u32>, }
Kun je structs met custom-velden of berekende waarden (bijvoorbeeld fn get_hash()) serialiseren?
Ja, maar dergelijke velden moeten worden gemarkeerd met #[serde(skip)], als ze niet moeten worden geserialiseerd, of je moet Serialize/Deserialize handmatig implementeren als je berekende waarden wilt serialiseren.
Wat gebeurt er als de structuur in Rust niet overeenkomt met de velden van het JSON-object (vermissing van een veld of een nieuw veld verschijnt)?
Standaard negeert serde extra velden in JSON (tenzij de strikte modus is ingeschakeld) en geeft een foutmelding bij het ontbreken van verplichte velden in de structuur (tenzij #[serde(default)] is opgegeven).
Een externe JSON komt binnen met extra en ontbrekende velden, en de structuur gebruikt geen #[serde(default)],
Voordelen:
Nadelen:
Gebruik van #[serde(default)] en #[serde(skip_serializing_if)], alle velden worden gevalideerd, extra worden genegeerd.
Voordelen:
Nadelen: