En Rust, la serialización y deserialización de datos son tareas bastante comunes, especialmente al integrarse con servicios web, bases de datos o al intercambiar mensajes entre componentes. La biblioteca más popular para esto es serde, que se ha convertido en el estándar de facto para trabajar con la serialización en Rust.
Historia de la cuestión
Los desarrolladores de Rust se encontraron con la necesidad de una serialización flexible y eficiente. Inicialmente, había una gran cantidad de soluciones especializadas, pero fue serde quien ofreció una integración conveniente con los macros derive, y un esquema flexible de soporte para varios formatos (JSON, CBOR, BSON, TOML, YAML y otros).
Problema
Es necesario garantizar la correcta transformación de las estructuras de Rust a los formatos de intercambio de datos (por ejemplo, JSON) y viceversa, sin perder la seguridad de tipos y evitando "errores silenciosos". Los problemas a menudo son causados por la falta de coincidencia de la estructura de datos o por intentar serializar tipos no admitidos.
Solución
Para serializar estructuras, se deben implementar los traits Serialize y Deserialize, generalmente usando #[derive(Serialize, Deserialize)]. Para los casos no estándar, se pueden implementar estos traits manualmente o usar atributos que controlen el esquema de serialización.
Ejemplo de código:
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); }
Características clave:
¿Cómo serializar/deserializar campos Option o campos con valores predeterminados?
Se puede usar #[serde(default)] para especificar un valor predeterminado o #[serde(skip_serializing_if = "Option::is_none")], para no serializar None.
Ejemplo de código:
#[derive(Serialize, Deserialize)] struct Config { #[serde(default)] timeout: Option<u32>, }
¿Se pueden serializar estructuras con campos personalizados o valores calculados (por ejemplo, fn get_hash())?
Sí, pero esos campos deben marcarse con #[serde(skip)], si no deben ser serializados, o implementar Serialize/Deserialize manualmente, si se desea serializar valores calculados.
¿Qué sucede si la estructura en Rust no coincide en los campos con el objeto JSON (falta un campo o aparece uno nuevo)?
Por defecto, serde ignora campos adicionales en JSON (si no se activa el modo estricto) y generará un error si faltan campos obligatorios de la estructura (a menos que se haya establecido #[serde(default)]).
Llega un JSON externo con campos adicionales y faltantes, y la estructura no utiliza #[serde(default)],
Pros:
Contras:
Se utiliza #[serde(default)] y #[serde(skip_serializing_if)], todos los campos son validados, y se ignoran los adicionales.
Pros:
Contras: