serde es el framework de serialización más usado en Rust: convierte estructuras a JSON, TOML, YAML, MessagePack y otros formatos, y al revés. La clave es que todo el trabajo pesado ocurre en tiempo de compilación mediante macros derive. En runtime no hay reflexión ni overhead de inspección de tipos.
Instalación
// Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Derive básico en structs
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Usuario {
id: u32,
nombre: String,
email: String,
activo: bool,
}
fn main() -> serde_json::Result<()> {
let usuario = Usuario {
id: 1,
nombre: "Ana García".into(),
email: "[email protected]".into(),
activo: true,
};
// Struct ? JSON
let json = serde_json::to_string_pretty(&usuario)?;
println!("{json}");
// JSON ? Struct
let json_entrada = r#"{"id":2,"nombre":"Luis","email":"[email protected]","activo":false}"#;
let u2: Usuario = serde_json::from_str(json_entrada)?;
println!("{u2:?}");
Ok(())
}
Atributos de campo
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Producto {
#[serde(rename = "product_id")] // nombre distinto en JSON
id: u32,
nombre: String,
#[serde(skip_serializing_if = "Option::is_none")] // omitir si None
descripcion: Option<String>,
#[serde(default)] // usar Default si falta en el JSON
en_stock: bool,
#[serde(skip)] // ignorar completamente en ambas direcciones
cache_interno: String,
}
fn main() -> serde_json::Result<()> {
let p = Producto {
id: 42,
nombre: "Teclado mecánico".into(),
descripcion: None,
en_stock: true,
cache_interno: "ignorado".into(),
};
let json = serde_json::to_string(&p)?;
// {"product_id":42,"nombre":"Teclado mecánico","en_stock":true}
// descripcion y cache_interno no aparecen
println!("{json}");
Ok(())
}
Enums con serde
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "tipo", content = "datos")] // representación adyacente
enum Evento {
Click { x: i32, y: i32 },
Teclado { tecla: String, modificadores: Vec<String> },
Scroll { delta: f64 },
}
fn main() -> serde_json::Result<()> {
let evento = Evento::Click { x: 100, y: 200 };
let json = serde_json::to_string_pretty(&evento)?;
// {"tipo":"Click","datos":{"x":100,"y":200}}
println!("{json}");
let e2: Evento = serde_json::from_str(
r#"{"tipo":"Teclado","datos":{"tecla":"Enter","modificadores":["Ctrl"]}}"#
)?;
println!("{e2:?}");
Ok(())
}
serde_json::Value para JSON dinámico
use serde_json::{Value, json};
fn main() -> serde_json::Result<()> {
// Construir JSON sin struct
let payload = json!({
"nombre": "API Rust",
"version": 2,
"activo": true,
"etiquetas": ["rust", "api", "json"]
});
println!("{payload}");
// Navegar el JSON
if let Some(nombre) = payload["nombre"].as_str() {
println!("Nombre: {nombre}");
}
// Parsear JSON arbitrario
let texto = r#"{"desconocido": [1, 2, {"clave": "valor"}]}"#;
let v: Value = serde_json::from_str(texto)?;
println!("{}", v["desconocido"][2]["clave"]); // "valor"
Ok(())
}
Reutilizar los derives con TOML y YAML
// Cargo.toml
// toml = "0.8"
// serde_yaml = "0.9"
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Config {
host: String,
puerto: u16,
debug: bool,
}
fn main() -> anyhow::Result<()> {
let config_toml = r#"
host = "localhost"
puerto = 8080
debug = true
"#;
// El mismo struct funciona con TOML
let config: Config = toml::from_str(config_toml)?;
println!("{config:?}");
// Y con YAML
let config_yaml = "host: localhostnpuerto: 8080ndebug: truen";
let config2: Config = serde_yaml::from_str(config_yaml)?;
println!("{config2:?}");
Ok(())
}
Resumen
#[derive(Serialize, Deserialize)]genera el código de serialización en compilación: cero overhead en runtime.serde_json::to_stringyfrom_strson los puntos de entrada más comunes.- Los atributos de campo (
rename,skip,default,skip_serializing_if) controlan la representación JSON sin tocar la lógica. serde_json::Valuey el macrojson!permiten trabajar con JSON sin structs tipadas.- El mismo
#[derive(Serialize, Deserialize)]funciona con cualquier formato que tenga un crate serde-compatible: JSON, TOML, YAML, CBOR, MessagePack, etc.
