En Rust el XML se maneja con quick-xml, que ofrece dos caminos: recorrer el documento etiqueta por etiqueta, o declarar una estructura y dejar que serde la rellene. Esta página usa el segundo, que es el que resuelve la mayoría de los casos con menos código.
También te puede interesar
La dependencia
cargo add quick-xml --features serialize cargo add serde --features derive
[dependencies]
quick-xml = { version = "0.42.0", features = ["serialize"] }
serde = { version = "1.0.229", features = ["derive"] }
De XML a structs y de vuelta
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename = "inventario")]
struct Inventario {
#[serde(rename = "articulo")]
articulos: Vec<Articulo>,
}
#[derive(Debug, Deserialize, Serialize)]
struct Articulo {
#[serde(rename = "@id")]
id: u32,
nombre: String,
unidades: u32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let xml = r#"<inventario>
<articulo id="1"><nombre>tornillos</nombre><unidades>120</unidades></articulo>
<articulo id="2"><nombre>tuercas</nombre><unidades>80</unidades></articulo>
</inventario>"#;
let inv: Inventario = quick_xml::de::from_str(xml)?;
for a in &inv.articulos {
println!("{} | {:<10} | {} unidades", a.id, a.nombre, a.unidades);
}
let salida = quick_xml::se::to_string(&inv)?;
println!("\nde vuelta a XML:\n{salida}");
Ok(())
}
salida1 | tornillos | 120 unidades 2 | tuercas | 80 unidades de vuelta a XML: <inventario><articulo id="1"><nombre>tornillos</nombre><unidades>120</unidades></articulo><articulo id="2"><nombre>tuercas</nombre><unidades>80</unidades></articulo></inventario>
La diferencia con JSON está en los nombres. XML distingue entre atributos y elementos, y serde necesita saber cuál es cuál: el prefijo @ marca un atributo, como en #[serde(rename = "@id")]. Sin esa arroba, serde buscaría un elemento <id> y no lo encontraría.
Y el nombre del elemento raíz. Sin
#[serde(rename = "inventario")] sobre la estructura, el XML generado sale como <Inventario> con mayúscula, porque serde usa el nombre del tipo de Rust, que por convención va en mayúscula inicial. Para leer da igual, pero al escribir produce un documento que no coincide con el original. Conviene ponerlo siempre.Correspondencias entre XML y Rust
| En el XML | En la estructura |
|---|---|
| Un atributo | #[serde(rename = "@id")] |
| Un elemento hijo | un campo con el mismo nombre |
| Varios hijos iguales | un campo Vec<T> |
| Un hijo que puede faltar | un campo Option<T> |
| El texto dentro de la etiqueta | #[serde(rename = "$text")] |
| Cambiar el nombre de la raíz | #[serde(rename = "...")] en el struct |
Cuándo no usar serde para XML. Este camino va bien con documentos de forma conocida y regular. Si el XML es enorme y no cabe en memoria, o la estructura es irregular y llena de excepciones, conviene el lector de eventos de quick-xml, que recorre el documento sin construirlo entero. Ten en cuenta que ese API cambia entre versiones con cierta frecuencia: en la 0.42 varios métodos que antes devolvían bytes devuelven ya cadenas.
El código de esta página se compiló y ejecutó con rustc 1.96.1 y quick-xml 0.42.0 antes de publicarla; las salidas están copiadas de la ejecución real. Documentación oficial: quick-xml en docs.rs.